Open Booking API 1.0 CR2

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 CR2

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, independent of the Seller, that provides an application allowing Customers to make bookings. For simplicity the API client is referred to as the Broker throughout this specification, however other API clients are permissible, and "Booking Partner" is the collective term for different types of clients of this API.
Booking System
The organisation 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 an Order UUID, which is used to uniquely identify the OrderQuote / 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 capability 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 an Order UUID.
  • 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 Order UUID MUST be regenerated to allow for a clean retry of the booking. The Order UUID MUST NOT be regenerated for expected errors (status code 4xx).

ii) C1 - Broker call with OrderQuote including the Order 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 Order UUID if it is supported, otherwise it provides a simple availability and totalPaymentDue confirmation. An OrderQuote response with HTTP Status 200 MUST 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.
  • The OrderQuote response MUST include totalPaymentDue, which allows the Broker to update any UI with the total.
  • The OrderQuote response 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, but excluding those places already reserved for this Order UUID by previous calls to C1 and C2.
  • The OrderQuote response 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.
  • If a viable payment reconciliation route is available between the Broker and the Seller that requires more than just the identifier property of the Payment to be included in B, the payment property MUST be included at C1, as per Payment reconciliation detail validation. This ensures that a viable payment reconciliation route is available before the Customer starts to enter their data.
  • 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 consent to the most recently modified version of such terms. The Broker MAY remember the consent previously given by the Customer based on the url and dateModified of the Terms, only if dateModified is provided, and if consent for storing such preferences is given by the Customer.
  • attendee and orderItemIntakeFormResponse data is be captured at this stage if specified via attendeeDetailsRequired and orderItemIntakeForm in the C1 response, respectively.
  • If payment initiation is optional, 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 the Order 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 Order 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.
  • If a viable payment reconciliation route is available between the Broker and the Seller that requires more than just the identifier property of the Payment to be included in B, the payment property MUST be included at C2, as per Payment reconciliation detail validation.
  • 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 payment is not to be initiated or totalPaymentDue is 0, payment details MUST NOT be captured.

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

  • The Order @id, which includes the Order 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 was not initiated or totalPaymentDue is 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 Order 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 the Order 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 was not initiated or totalPaymentDue is 0, the payment property MUST NOT be included in the Order request or response, and the Booking System MUST proceed to create a payment-free Order.

  • Error responses MUST be returned for any Payment Error Conditions.

  • 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 Order UUID. As the call is idempotent, the Booking System will need to track the Order 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 Order 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, an Order UUID clash is assumed, a 500 response of with OrderAlreadyExistsError is returned by the Booking System, and a new Order 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 Order UUID MUST be regenerated to allow for a clean retry of the booking. The Order UUID MUST NOT be regenerated for expected errors (status code 4xx).

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

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 was not initiated or totalPaymentDue is 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 Order 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 an 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 Order UUID.

Note that the Order 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 an Order UUID, and use it consistently during the booking flow.
  • The Broker MUST NOT reuse Order 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 consent to the most recently modified version of such terms, identified by dateModified and the url.
  • The Broker MUST handle payments and reconciliation with the Seller and the Payment Provider.
  • Calls to C1, C2, and B MUST include the @id of the seller to which requested OrderItems pertain, noting that it is not possible for Opportunities from different Sellers to be combined in the same Order.
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 Order 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 Order 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.
  • The Booking System MUST ensure that all endpoints defined in this specification are idempotent.
  • The Booking System MUST include the full Seller details in the seller property of the response at C1, C2, and B, as such details are not included within the opportunity data properties.

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 an Order UUID, which is used to uniquely identify the OrderQuote / OrderProposal / 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 the openBookingFlowRequirement of any Offer referenced in the OrderProposal request includes https://openactive.io/OpenBookingNegotiation within the open data, 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.

    The Booking System MUST generate a new OrderProposal Version UUID, which is a unique UUID string representation strictly as defined in [RFC4122], separate to the Order UUID, for the initial OrderProposal, and subsequently for each amendment made (if Proposal Amendment is supported).

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 within its open data 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 payment is not to be initiated or totalPaymentDue is 0, 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 Order 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 Order 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 MUST be atomic.

  • The returned OrderProposal also MUST contain an orderProposalVersion, which is constructed from the @id of the OrderProposal, combined with an additional OrderProposal Version UUID.

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

  • 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 request MAY include an orderCustomerNote in order to allow the Customer to ask a question about this Opportunity, as the beginning of a Message Exchange. This is only permissible if the any Offer referenced in the OrderProposal includes an openBookingFlowRequirement property within the open data that contains 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 MUST 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 was not initiated or totalPaymentDue is 0, the payment property MUST NOT be included in the OrderProposal request or response.

    • Error responses MUST be returned for any Payment Error Conditions.

    • 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 Order UUID. As the call is idempotent, the Booking System will need to track the Order 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 Order 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 Order UUID clash is assumed, a 500 response of with OrderAlreadyExistsError is returned by the Booking System, and a new Order 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 Order UUID MUST be regenerated to allow for a clean retry of the booking. The Order 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 in the revised OrderProposal, payment initiation is now optional, 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.

  • If payment details were not previously captured and payment is now to be initiated, due to the revised OrderProposal, they MUST be captured. If totalPaymentDue is 0, payment details MUST NOT be captured.

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

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

  • If a newer version of the OrderProposal is available compared with the orderProposalVersion provided by the Broker, then B MUST return an OrderProposalVersionOutdatedError with associated status code.

  • 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. The Booking System MUST NOT reflect back the orderProposalVersion in its response to B.

  • All other behaviour is identical to the Simple Booking Flow step vii. The Broker stores the response to B to completely replace their previously stored OrderProposal, and the previous OrderProposal is set to "state": "deleted" in the Orders feed. The orderProposalVersion MUST NOT be included in the new Order.

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.
  • Calls to P MUST include the @id of the seller to which requested OrderItems pertain.
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.
  • The Booking System MUST include the full Seller details in the seller property of the response at P.

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.

If the same attendee is used across multiple OrderItems, a duplicate Person MUST be provided for each of these OrderItems. The Broker may choose to design their user interface to minimise repeat data entry (e.g. by allowing the Customer to enter attendee details first, then later assigning them to OrderItems), however this is outside the scope of the specification.

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 Offer 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 Offer 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 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 on each occasion that a set of orderItems within an Order are 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, the Broker MUST generate a new Invoice in the form of a tax receipt, on the basis of the response from B. To ensure that the Customer is provided with the correct tax receipt, if totalPaymentTax is provided in an Order from B or subsequently 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.

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.2.1 Payment reconciliation detail validation

Payment reconciliation detail validation is required if any viable payment reconciliation route exists between the Broker and the Seller that necessitates more than just the identifier property of the Payment being included in B. For simplicity of implementation, such validation is required even if the totalPaymentDue is likely to be 0 or the prepayment value of the selected Offers indicates no payment.

This ensures that a viable payment reconciliation route is available before the Customer starts to enter their data.

To achieve such validation, the payment property MUST be included at C1 and C2 by the Broker in the OrderQuote request, including all relevant properties of Payment except the identifier.

If the payment details supplied (e.g. accountId) are not considered valid for reconciliation by the Booking System then a InvalidPaymentDetailsError MUST be returned at C1 and C2.

For the Booking System, the payment property at C1 and C2 is always considered optional, as a payment reconciliation route may not exist.

At B and P the payment property MUST only be included if a payment is actually being processed. 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 the Order at B. In all other cases payment property MUST be included in the Order at B.

For the simple case, if the Booking System does not implement properties other than the identifier property of the Payment, the Payment MUST simply be reflected back in the response to C1, C2, B and P with only the identifier property included, and no implementation of the InvalidPaymentDetailsError response is required.

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 either 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 identifiers

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 Booking without payment

If the OPTIONAL prepayment feature of this specification is supported by the Booking System, the following logic MUST be applied by the Booking System to populate the prepayment value of totalPaymentDue in the OrderQuote response of C1 or C2:

acceptedOffers across OrderItems Booking System sets prepayment value of totalPaymentDue
Any prepayment values are either https://openactive.io/Required or omitted where price values are also not 0 https://openactive.io/Required
At least one prepayment value is https://openactive.io/Optional and the rest are either https://openactive.io/Unavailable or omitted where price values are also 0 https://openactive.io/Optional
In all cases either prepayment value is https://openactive.io/Unavailable or price value is 0 https://openactive.io/Unavailable

If the prepayment property is not supported by the Booking System, the prepayment property MUST simply be omitted in all cases, however the Booking System MUST still handle the payment error conditions.

The following logic MUST be applied by the Broker to determine whether to initiate taking payment from the Customer, and applies whether or not the Booking System supports prepayment:

totalPaymentDue Broker initiates payment
prepayment is https://openactive.io/Required, or omitted and price is not 0 Always
prepayment is https://openactive.io/Optional The Customer MAY be given the option, at the Broker's discretion
prepayment is https://openactive.io/Unavailable or price is 0 Never

If payment is not to be initiated based on the above:

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

For Offers with a price of 0:

  • The prepayment property of the Offer MUST be either https://openactive.io/Unavailable or unspecified.
  • The Offer MAY not include priceCurrency.
  • If a priceCurrency is not available then Brokers SHOULD communicate "Free" or equivalent when referencing a zero price.

When an Order or OrderProposal involves exclusively Free Opportunities the totalPaymentDue will be 0 regardless of the prepayment property values. In this case, no interaction with a Payment Provider is required, the payment property MAY be omitted at C1 and C2 (depending on Payment Reconciliation Detail Validation), and MUST be omitted at B or P.

7.6.2 Payment error conditions

At B or P the Booking System MUST produce an error response in the following cases:

  • If the payment property is expected in the request (as within totalPaymentDue: prepayment is https://openactive.io/Required, or if prepayment not supported, price is not 0) but not provided, then a MissingPaymentDetailsError MUST be returned .
  • If the payment property is provided but not expected in the request (as within totalPaymentDue: prepayment is https://openactive.io/Unavailable or price is 0), then an UnnecessaryPaymentDetailsError MUST be returned.
  • If the payment property does not include identifier, 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.

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 P or B. When an OfferOverride is used, it MUST be used consistently between C1, C2 and P or 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.

The use of OfferOverride always creates the behaviour equivalent to advanceBooking and prepayment both being set to https://openactive.io/Required, and availableChannel having the value of https://openactive.io/OpenBookingPrepayment.

latestCancellationBeforeStartDate does not apply to OfferOverride, and is treated as unspecified. Hence cancellation is permissible based on the value allowCustomerCancellationFullRefund alone.

The Booking System MUST document the most comprehensive expected equivalent value of openBookingFlowRequirement that is applicable when an OfferOverride is requested by the Broker. This allows such Brokers to understand the features of this specification that may be used by the Booking System, and hence assess whether they are capable of completing the booking flow using OfferOverride.

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

7.8 Currency support

This version of the specification does not support mixed currencies.

All priceCurrency values within an Order, OrderQuote, or OrderProposal must be identical.

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"), from the Opportunity Data [RPDE] open feeds. 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 OpportunityOfferPairNotBookableError against each OrderItem in the OrderQuote that is not bookable.

The Booking System MUST include the openBookingFlowRequirement property within the Offers in the Opportunity Data [RPDE] open feeds, only if any features of the specification covered by this property are required to book the Offer. This allows Brokers that do not support the specified features to easily filter out such Offers from their experience. The openBookingFlowRequirement property MUST NOT be included in the request or response of any of the endpoints defined within this specification.

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 ageRestriction 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 an array of oa:additionalAdmissionRestriction on the SessionSeries, FacilityUse, Event, HeadlineEvent or CourseInstance, and the Broker MUST communicate these restrictions clearly during the booking process.

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

8.1.2 Opportunity data properties

For requests to C1, C2, P, and B, and for items in the Orders feed, the Opportunity Data within the orderedItem property of an OrderItem MUST only include the properties @type and @id. The orderedItem property of all OrderItems MAY be excluded from the Orders feed if the Booking System does not support Replacement.

For responses from C1, C2, P, and B, and from the Order Status endpoint, the Opportunity Data within the orderedItem property of an OrderItem MUST correspond exactly with the model used within the Opportunity Data [RPDE] open feeds. For example if properties within a SessionSeries and ScheduledSession are used in the open feeds, corresponding properties must be used within objects of the same types within the orderedItem.

See the [Modelling-Opportunity-Data] specification for more information on these types.

For such responses, the Opportunity Data MUST include the following properties:

  • @type
  • @id and name of the bookable Opportunity
  • url
  • location (including name, and either or ideally both of address or geo)
  • activity
  • startDate

For such responses, the following properties are RECOMMENDED:

  • endDate
  • duration
  • ageRange
  • genderRestriction
  • eventStatus
  • maximumAttendeeCapacity or maximumUses
  • remainingAttendeeCapacity or remainingUses

For such responses, the Opportunity Data MUST NOT include the following properties:

  • offers (as the relevant Offer will be returned in acceptedOffer)
  • subEvent or event (as the opportunity should be bookable, and therefore of sufficient granularity)
  • organizer or provider (as this is provided by the seller property of the Order)

Where relevant it is expected that the Opportunity Data for such responses includes superEvent or facilityUse to allow for a SessionSeries or FacilityUse to be referenced from a SessionSeries or Slot, for example.

8.1.3 acceptedOffer properties

The Booking System MUST include all applicable properties within the acceptedOffer consistently in responses to C1, C2, P, and B. acceptedOffer MUST also include the same properties in the Orders feed, unless the Booking System does not support Replacement.

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 Order 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 Order 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 Order 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 added or removed inside 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 (e.g. when an attendee arrives early), is currently outside the scope of this specification.

Hence within this version of the specification any OrderItem @id submitted to the Order Cancellation endpoint MUST reference an Opportunity with a startDate in the future.

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 orderItemStatus is not yet set to https://openactive.io/CustomerAttended, and allowCustomerCancellationFullRefund is true, and either latestCancellationBeforeStartDate is unspecified and startDate is in the future, or 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 to the Order Cancellation endpoint 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 Customer, 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 and OrderProposals, 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 and OrderProposals that have been updated by the Booking System subsequent to successful completion of B or P, respectively. 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 or OrderProposal item is found by the Broker on the feed. Due to the Order or OrderProposal being stored during the Payment Authorisation stage, the Broker should always have a matching Order or OrderProposal for any Order UUID on the Orders feed even if it has been soft-deleted. This would make an unrecognised Order or OrderProposal an impossible scenario, and hence an unrecognised Order or OrderProposal should be logged as a serious error within the Broker and promptly investigated.

The RPDE id of an OrderProposal MUST differ from the RPDE id of its related Order. The OrderProposal is marked as deleted at B, and the related Order will not appear in the Orders feed until it is next updated.

An Order MUST NOT be set to "state": "deleted" in the Orders feed by the Booking System, except as a result of Order Deletion, or when authorisation to access a particular Seller has been revoked. An Order in the Orders feed marked as deleted outside of Order Deletion should be logged as a serious error within the Broker and promptly investigated, and SHOULD NOT trigger an immediate deletion of the Order by the Broker, to allow for the issue to be resolved.

8.4.2 Order stores with eventual consistency

It is important that the storage used by the Broker for Orders provides a linearisability 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 and OrderProposals 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 or OrderProposal in the feed MUST result in a change to the RPDE modified property associated with it.

8.4.3.1 totalPaymentDue invariant

In this version of the specification, the following constraints exist around the stored Order that are shared by both the Broker and the Booking System:

  • OrderItems MUST NOT be added or removed inside an existing Order after B.
  • The Opportunity (orderedItem) and Offer (acceptedOffer) of an OrderItem referenced within an Order in the Orders feed MAY be updated to reference a different bookable Opportunity and Offer pair via Replacement, only if the price is less than or equal the previous Offer price, and if the prepayment value is the same.
  • The acceptedOffer and unitTaxSpecification properties of an OrderItem referenced within an Order MUST always represent a snapshot of the Offer at the point it was booked, or the last Replacement.
  • OrderItem cancellation MUST NOT be reversed.

Hence an invariant exists whereby the totalPaymentDue of an Order in the Orders feed MUST only ever decrease or remain unchanged due to OrderItem Cancellation or Replacement, and MUST NOT otherwise change or increase.

These constraints and invariant do not apply to OrderProposals.

8.4.4 Broker: Processing the Orders feed

The Broker MUST maintain an up-to-date store of Orders and OrderProposals received from the Booking System. Each Order or OrderProposal received is compared to the last version stored by the Broker, and any differences between the old and new content can 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 and OrderProposals as durable, and not simply a cache, to ensure their Customers always get relevant notifications.

The Order/OrderProposal properties included in the Orders feed MUST be a subset of the Order/OrderProposal properties returned in the response from B or P as specified below (effectively making the Orders feed a series of partial update 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.

The following properties of the Order or OrderProposal MUST be included in the Orders feed, and the Broker MUST overwrite its previous copy of each property when processed:

  • totalPaymentTax (if taxCalculationExcluded is not true)
  • totalPaymentDue
  • orderProposalStatus (OrderProposal only)
  • orderProposalVersion (OrderProposal only)

Additionally, the orderedItem property of the Order or OrderProposal MUST be included in the Orders feed. Noting the exceptions outlined in this section, all other properties of the OrderItems MUST overwrite the Broker's previous copy of each when processed, and if a property is no longer present the Broker must remove it. This includes the following properties, and any additional supported extensions:

For OrderItem in both Order and OrderProposal:

  • orderItemStatus
  • allowCustomerCancellationFullRefund
  • accessPass
  • accessCode
  • additionalProperty
  • cancellationMessage
  • customerNotice
  • acceptedOffer
  • unitTaxSpecification
  • orderedItem (including only @type and @id properties)

And for OrderItem in OrderProposal it additionally includes:

  • attendeeDetailsRequired
  • attendee
  • orderItemIntakeForm
  • orderItemIntakeFormResponse

As an exception to the above for Orders, if the orderedItem property is not present in the OrderItems in the Orders feed (in the case that the Booking System does not support Replacement), the following properties are assumed to be immutable for that Order, and the Broker MUST NOT overwrite their previous copy of each (taken at B) when processing the Orders feed:

  • acceptedOffer
  • unitTaxSpecification
  • orderedItem

The following properties MUST always be included in the Orders feed for the convenience of the Broker, and MUST be immutable:

  • @type, @id and identifier (the Order UUID) of the Order or OrderProposal
  • @type and @id of the OrderItem
  • taxCalculationExcluded (only included if its value is true)

Additionally any properties within objects included in the properties above that were included in the original response from B or P MUST be included in the Orders feed, with the exception of the following.

The following properties MUST NOT be included in the Orders feed and MUST always be maintained by the Broker and not overwritten; hence changes to these properties MUST NOT be permitted after B:

Properties in Order and OrderProposal:

  • bookingService
  • broker
  • brokerRole
  • seller
  • customer
  • orderNumber
  • payment

Properties in OrderItem within Order only:

  • attendeeDetailsRequired
  • attendee
  • orderItemIntakeForm
  • orderItemIntakeFormResponse

Opportunity Data updates for Orders MUST NOT be read from the Orders feed, and must instead be updated using the Opportunity Data [RPDE] open feeds as specified in Change of logistics notification.

Brokers reading OrderItems in OrderProposals, or Replacements, from the Orders feed MUST look up the @type and @id of each orderedItem against the Opportunity Data [RPDE] open feeds to retrieve the most up-to-date full Opportunity.

The Broker is expected to fully replace properties at the Order/OrderProposal level, in their entirety, as specified above, and MUST NOT perform a granular PATCH of nested properties within these. The rationale for this is that both this specification and the [Modelling-Opportunity-Data] specification on which this specification is based do not allow for explicit null values to exist within the model, which impedes property removal, and hinders any straightforward implementation of a granular PATCH.

Note that in this version of the specification, once an Order or OrderProposal has been created, only properties that are present in the Orders feed MAY be updated.

Note also that the representation of Orders within the Orders feed is designed specifically such that it does not include any personal data, except for that which is already available from the Opportunity Data [RPDE] open feeds provided by the Booking System.

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.

OrderProposals MUST be stored by the Broker based on a successful response to P, and Orders MUST be stored by the Broker based on a successful response to B.

New Orders and OrderProposals MUST NOT be included in the feed until they have been updated at least once. This minimises the volume of updates 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, replacement, refund calculation and notification

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

There are two scenarios that can update the effective OrderItem composition of an Order found in the Orders feed:

  • Cancellation: Updated https://openactive.io/CustomerCancelled or https://openactive.io/SellerCancelled values for orderItemStatus, indicating the item is cancelled and a refund due if necessary.
  • Replacement: Updated @type and @id within the orderedItem, and associated updates to acceptedOffer and unitTaxSpecification, indicating the item has been replaced by the Seller with an equivalent item of the same or lower price.

The Broker MUST process refunds for each new Cancellation or Replacement 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 a Cancellation, the snapshot contents of acceptedOffer (and unitTaxSpecification, if relevant) within the OrderItems MUST remain unchanged by the Booking System after B, and new totalPaymentDue (and totalPaymentTax, if relevant) values MUST be calculated excluding OrderItems with an orderItemStatus of either https://openactive.io/CustomerCancelled or https://openactive.io/SellerCancelled.

For a Replacement, an updated acceptedOffer (and unitTaxSpecification, if relevant) MAY be included in the OrderItem, and MUST then constitute a snapshot and MUST remain unchanged by the Booking System except for in the case of a further Replacement. The value of prepayment within the acceptedOffer MUST NOT be change through Replacement.

If the totalPaymentDue value of the Order has decreased (it is not permitted to increase) following one or more Cancellations or Replacements within an Order, 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.

In all cases of the Order composition changing, the Broker MUST generate a new Invoice for the Order to replace the previous Invoice.

The Customer MUST be notified after a refund is successfully processed, with the Broker using the orderItemStatus or updated orderedItem to indicate the source of the refund.

If refund processing takes longer than 1 minute, then the Customer must be notified immediately of a Cancellation or Replacement ahead of the notification of the completed refund. This ensures the Seller can rely on the Broker as a notification channel. The Customer MUST be notified with the cancellationMessage (for Cancellation) or customerNotice (for Replacement) for the OrderItem, if provided.

Note that the Customer MUST be notified in the event of all Cancellations or Replacements, even if no refund is due (e.g. for Free Opportunities).

If the Booking System does not support Replacement, it MAY consistently exclude all three of the properties orderedItem, acceptedOffer and unitTaxSpecification from all OrderItems in the Orders feed.

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 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 requires the Orders feed to not include the full contents of the Opportunities within the orderedItem, and instead only includes the Opportunity properties @type and @id. Additionally the orderedItem property of the OrderItem is only required within the Orders feed if Replacement is supported.

Hence the Broker MUST store the Opportunity references @type and @id at B, MUST monitor the Opportunity Data open feeds for updates to the referenced information, and MUST provide a notification to the Customer for substantive changes to the same Opportunity; specifically: any changes to the Opportunity name, startDate, endDate, duration or meetingPoint properties, and any changes to the name, address or geo properties within location.

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.4.11 Other notifications

The following properties within Orders in the Orders feed MAY also be updated by the Booking System, and MUST trigger the Broker to notify the Customer:

  • accessCode and/or accessPass - A notification that the access details for the Opportunity have changed

The following properties within Orders in the Orders feed MAY be updated by the Booking System, and MUST be reflected to the Customer in any user interface during their normal interaction with the Broker:

  • allowCustomerCancellationFullRefund - Automated cancellation with a full refund is no longer possible via the Broker.

The following properties within Orders in the Orders feed MAY also be updated by the Booking System, and MUST be used subsequently by the Broker for their internal operations:

  • additionalProperty - The details to use for payment reconciliation have changed

8.5 Amending the Order after B

OrderItems cannot be added or removed within an existing Order under any circumstances in this version of the specification, and are not permitted to be replaced by the Broker (only the Seller may provide a Replacement). Hence to make such changes, 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 newly generated Order 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 only be allowed to proceed if they have explicitly acted to consent to the most recently modified version of such terms, identified by dateModified and the url, before the Customer completes B.

The Broker MAY remember the consent previously given by the Customer based on the url and dateModified of the Terms, only if dateModified is provided, and if consent for storing such preferences is given by the Customer.

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",
    "dateModified": "2020-02-16T20:31:13Z",
    "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 accessPass. An accessCode SHOULD be provided for manual use in the event that an accessPass fails.

Each accessCode and accessPass 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 accessPass MUST NOT be supplied.

A Broker MUST make all supplied accessPasses 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 accessPass 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 accessPass
"accessPass": [
  {
    "@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 accessPass 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.

Additionally where use of an existing barcode is desirable (for example, the barcode on a Broker's own membership card) the Broker MAY include a Barcode in the accessPass property against individual OrderItems in the request to B. If the Booking System is able to accept such existing barcodes, it SHOULD reflect back the specified Barcode in the accessPass array in the response along with any other valid access control options.

Example 22: Example of Barcode in accessPass
"accessPass": [
  {
    "@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 API discovery, using the [Dataset-API-Discovery] specification as a basis. This allows a Base URI, as defined in [RFC3986], to be discovered by the Broker, and for such a Base URI to vary based on the naming conventions of each Booking System.

All Booking System endpoints defined in this specification MUST be idempotent.

9.1 Paths and Verbs

Due to API discovery, the paths specified are relative to the Base URI. This Base URI must be used consistently within an implementation. It is the responsibility of the Booking System to advertise the Base URI via the [Dataset-API-Discovery] specification.

The absolute URL of the Orders feed MAY be separately discoverable, independently of the Base URI, provided that such discovery is consistent with the [Dataset-API-Discovery] specification. If such an absolute URL is available, it MUST be used instead of the relative path defined within this specification.

With the exception of the Orders feed, all endpoints MUST accept an Order UUID as the final segment of their path. This is indicated by the placeholder {uuid} in the relative paths in the table below. The value of this Order UUID is supplied by the Broker, as described throughout this specification.

Brokers MUST only access the endpoints using the paths described in this specification, with the exception of the absolute URL of the Orders feed, if available.

A summary of endpoints defined by this specification is provided:

Endpoint Name Status Returns Body Resource HTTP Verb Relative 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 implementations, C1 and C2 can be handled by same underlying code.

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 Order 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 Order 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 Order 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. If persisted, /order-quote-templates appear immediately in /order-quotes, in order that they can be deleted via OrderQuote Deletion.
  • /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 Relative 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 can be handled by the same underlying code 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, much of the business logic can be shared between 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",
      "position": 0,
      "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",
  "orderRequiresApproval": false,
  "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",
        "requiresExplicitConsent": false
      },
      {
        "@type": "TermsOfUse",
        "name": "Terms and Conditions",
        "url": "https://example.com/terms-and-conditions",
        "dateModified": "2019-04-16T20:31:13Z",
        "requiresExplicitConsent": true
      }
    ]
  },
  "bookingService": {
    "@type": "BookingService",
    "name": "Playwaze",
    "url": "http://www.playwaze.com",
    "termsOfService": [
      {
        "@type": "Terms",
        "name": "Terms of Service",
        "url": "https://brokerexample.com/terms.html",
        "requiresExplicitConsent": false
      }
    ]
  },
  "lease": {
    "@type": "Lease",
    "leaseExpires": "2018-10-01T11:00:00Z"
  },
  "orderedItem": [
    {
      "@type": "OrderItem",
      "position": 0,
      "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://api.example.com/events/452",
          "name": "Bodypump",
          "activity": [
            {
              "type": "Concept",
              "id": "https://openactive.io/activity-list#5e78bcbe-36db-425a-9064-bf96d09cc351",
              "prefLabel": "Bodypump™",
              "inScheme": "https://openactive.io/activity-list"
            }
          ],
          "duration": "PT1H",
          "url": "https://example.com/events/452",
          "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 including any OrderItems that exclusively contain IncompleteAttendeeDetailsError or IncompleteIntakeFormError errors, and excluding any OrderItems that include any other 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",
  "orderRequiresApproval": false,
  "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",
        "requiresExplicitConsent": false
      },
      {
        "@type": "TermsOfUse",
        "name": "Terms and Conditions",
        "url": "https://example.com/terms-and-conditions",
        "dateModified": "2019-04-16T20:31:13Z",
        "requiresExplicitConsent": true
      }
    ]
  },
  "bookingService": {
    "@type": "BookingService",
    "name": "Playwaze",
    "url": "http://www.playwaze.com",
    "termsOfService": [
      {
        "@type": "Terms",
        "name": "Terms of Service",
        "url": "https://brokerexample.com/terms.html",
        "requiresExplicitConsent": false
      }
    ]
  },
  "lease": {
    "@type": "Lease",
    "leaseExpires": "2018-10-01T11:00:00Z"
  },
  "orderedItem": [
    {
      "@type": "OrderItem",
      "position": 0,
      "orderItemStatus": "https://openactive.io/OrderItemConfirmed",
      "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://api.example.com/events/452",
          "name": "Bodypump",
          "activity": [
            {
              "type": "Concept",
              "id": "https://openactive.io/activity-list#5e78bcbe-36db-425a-9064-bf96d09cc351",
              "prefLabel": "Bodypump™",
              "inScheme": "https://openactive.io/activity-list"
            }
          ],
          "duration": "PT1H",
          "url": "https://example.com/events/452",
          "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 Relative 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",
      "position": 0,
      "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",
  "orderRequiresApproval": false,
  "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",
        "requiresExplicitConsent": false
      },
      {
        "@type": "TermsOfUse",
        "name": "Terms and Conditions",
        "url": "https://example.com/terms-and-conditions",
        "dateModified": "2019-04-16T20:31:13Z",
        "requiresExplicitConsent": true
      }
    ]
  },
  "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",
        "name": "Terms of Service",
        "url": "https://brokerexample.com/terms.html",
        "requiresExplicitConsent": false
      }
    ]
  },
  "lease": {
    "@type": "Lease",
    "leaseExpires": "2018-10-01T11:00:00Z"
  },
  "orderedItem": [
    {
      "@type": "OrderItem",
      "position": 0,
      "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://api.example.com/events/452",
          "name": "Bodypump",
          "activity": [
            {
              "type": "Concept",
              "id": "https://openactive.io/activity-list#5e78bcbe-36db-425a-9064-bf96d09cc351",
              "prefLabel": "Bodypump™",
              "inScheme": "https://openactive.io/activity-list"
            }
          ],
          "duration": "PT1H",
          "url": "https://example.com/events/452",
          "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 including any OrderItems that exclusively contain IncompleteAttendeeDetailsError or IncompleteIntakeFormError errors, and excluding any OrderItems that include any other 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",
  "orderRequiresApproval": false,
  "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",
        "requiresExplicitConsent": false
      },
      {
        "@type": "TermsOfUse",
        "name": "Terms and Conditions",
        "url": "https://example.com/terms-and-conditions",
        "dateModified": "2019-04-16T20:31:13Z",
        "requiresExplicitConsent": true
      }
    ]
  },
  "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",
        "name": "Terms of Service",
        "url": "https://brokerexample.com/terms.html",
        "requiresExplicitConsent": false
      }
    ]
  },
  "lease": {
    "@type": "Lease",
    "leaseExpires": "2018-10-01T11:00:00Z"
  },
  "orderedItem": [
    {
      "@type": "OrderItem",
      "position": 0,
      "orderItemStatus": "https://openactive.io/OrderItemConfirmed",
      "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://api.example.com/events/452",
          "name": "Bodypump",
          "activity": [
            {
              "type": "Concept",
              "id": "https://openactive.io/activity-list#5e78bcbe-36db-425a-9064-bf96d09cc351",
              "prefLabel": "Bodypump™",
              "inScheme": "https://openactive.io/activity-list"
            }
          ],
          "duration": "PT1H",
          "url": "https://example.com/events/452",
          "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 Relative 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 Relative 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",
      "position": 0,
      "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/versions/8eb1a6ce-3f5b-40b0-87a7-bddb4c5518bd",
  "orderProposalStatus": "https://openactive.io/AwaitingSellerConfirmation",
  "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",
        "requiresExplicitConsent": false
      },
      {
        "@type": "TermsOfUse",
        "name": "Terms and Conditions",
        "url": "https://example.com/terms-and-conditions",
        "dateModified": "2019-04-16T20:31:13Z",
        "requiresExplicitConsent": true
      }
    ]
  },
  "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",
        "name": "Terms of Service",
        "url": "https://brokerexample.com/terms.html",
        "requiresExplicitConsent": false
      }
    ]
  },
  "orderedItem": [
    {
      "@type": "OrderItem",
      "position": 0,
      "orderItemStatus": "https://openactive.io/OrderItemProposed",
      "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://api.example.com/events/452",
          "name": "Bodypump",
          "activity": [
            {
              "type": "Concept",
              "id": "https://openactive.io/activity-list#5e78bcbe-36db-425a-9064-bf96d09cc351",
              "prefLabel": "Bodypump™",
              "inScheme": "https://openactive.io/activity-list"
            }
          ],
          "url": "https://example.com/events/452",
          "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
    }
  ],
  "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 Relative 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",
  "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 Relative 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",
      "position": 0,
      "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",
        "requiresExplicitConsent": false
      },
      {
        "@type": "TermsOfUse",
        "name": "Terms and Conditions",
        "url": "https://example.com/terms-and-conditions",
        "dateModified": "2019-04-16T20:31:13Z",
        "requiresExplicitConsent": true
      }
    ]
  },
  "bookingService": {
    "@type": "BookingService",
    "name": "Playwaze",
    "url": "http://www.playwaze.com",
    "termsOfService": [
      {
        "@type": "Terms",
        "name": "Terms of Service",
        "url": "https://brokerexample.com/terms.html",
        "requiresExplicitConsent": false
      }
    ]
  },
  "orderedItem": [
    {
      "@type": "OrderItem",
      "@id": "https://example.com/api/orders/e11429ea-467f-4270-ab62-e47368996fe8#/orderedItem/1234",
      "position": 0,
      "orderItemStatus": "https://openactive.io/OrderItemConfirmed",
      "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://api.example.com/events/452",
          "name": "Bodypump",
          "activity": [
            {
              "type": "Concept",
              "id": "https://openactive.io/activity-list#5e78bcbe-36db-425a-9064-bf96d09cc351",
              "prefLabel": "Bodypump™",
              "inScheme": "https://openactive.io/activity-list"
            }
          ],
          "url": "https://example.com/events/452",
          "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
            }
          }
        }
      },
      "accessPass": [
        {
          "@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/versions/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 Relative 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.

The corresponding Order MUST be marked as deleted in the Orders feed, only if it is already present in the feed (which is unlikely during the Simple Booking Flow).

When an Order is deleted the Booking System SHOULD hard delete it as though it had not been created in the first place (with only a stub deleted record remaining, for the Orders feed, if necessary), 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.

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 Relative 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/e11429ea-467f-4270-ab62-e47368996fe8#/orderedItem/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",
  "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 Relative Path
Orders feed REQUIRED Yes Orders feed GET /orders-rpde

This [RPDE] feed is described at length in the Orders feed section.

The Orders feed MUST NOT include the full contents of the Opportunities within the orderedItem, and instead MUST only include the Opportunity properties @type and @id, if orderedItem is included in the feed.

The acceptedOffer property within the Orders feed MUST include a snapshot of the Offer at the point it was booked or from the last Replacement, if acceptedOffer is included in the feed, as it reflects the Customer's purchase.

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": "https://example.com/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",
        "identifier": "e11429ea-467f-4270-ab62-e47368996fe8",
        "orderedItem": [
          {
            "@type": "OrderItem",
            "@id": "https://example.com/api/orders/e11429ea-467f-4270-ab62-e47368996fe8#/orderedItem/1234",
            "orderItemStatus": "https://openactive.io/OrderItemConfirmed",
            "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"
            },
            "accessPass": [
              {
                "@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
          }
        ]
      }
    }
  ]
}

9.2.10 Order Status

Endpoint Name Status Returns Body Resource HTTP Verb Relative 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.

This endpoint MUST include the full contents of the Opportunity within the orderedItem, as per Opportunity data properties.

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",
        "requiresExplicitConsent": false
      },
      {
        "@type": "TermsOfUse",
        "name": "Terms and Conditions",
        "url": "https://example.com/terms-and-conditions",
        "dateModified": "2019-04-16T20:31:13Z",
        "requiresExplicitConsent": true
      }
    ]
  },
  "bookingService": {
    "@type": "BookingService",
    "name": "Playwaze",
    "url": "http://www.playwaze.com",
    "termsOfService": [
      {
        "@type": "Terms",
        "name": "Terms of Service",
        "url": "https://brokerexample.com/terms.html",
        "requiresExplicitConsent": false
      }
    ]
  },
  "orderedItem": [
    {
      "@type": "OrderItem",
      "@id": "https://example.com/api/orders/e11429ea-467f-4270-ab62-e47368996fe8#/orderedItem/1234",
      "orderItemStatus": "https://openactive.io/OrderItemConfirmed",
      "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://api.example.com/events/452",
          "name": "Bodypump",
          "activity": [
            {
              "type": "Concept",
              "id": "https://openactive.io/activity-list#5e78bcbe-36db-425a-9064-bf96d09cc351",
              "prefLabel": "Bodypump™",
              "inScheme": "https://openactive.io/activity-list"
            }
          ],
          "url": "https://example.com/events/452",
          "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
            }
          }
        }
      },
      "accessPass": [
        {
          "@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"
  }
}

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.

The following sections include more detail about the properties available to describe each type. Some properties are considered to be essential to ensure the provision of a minimally useful set of information for a successful booking to be made. These REQUIRED properties MUST be provided, unless otherwise specified in the associated notes.

Other properties are marked as OPTIONAL or RECOMMENDED. Implementers SHOULD provide RECOMMENDED properties if it is feasible to do so, as they will improve the quality of the user experience that is achievable for both the Customer and Seller.

Where data is not available to populate RECOMMENDED and OPTIONAL properties, publishers MUST exclude these from their requests and responses. Requests and responses MUST NOT include properties with null values, empty strings or empty arrays.

Implementers MAY include additional properties. See the section on Handling Specification Dependencies and Extensions for more information.

Applications that implement this API MUST ignore any properties that they do not understand or do not support. This allows the API to evolve as required.

10.1 Order Model

10.1.1 schema:Order

Property Broker Request Booking System Response Type Notes
@type REQUIRED REQUIRED schema:Text Order
@id - REQUIRED URI A URI providing a unique identifier for the resource, which MUST be constructed as specified depending on the whether the resource was created at C1 / C2 ({baseUri}/order-quotes/{uuid}), P ({baseUri}/order-proposals/{uuid}) or B ({baseUri}/orders/{uuid}), based on the Base URI and Order UUID.
schema:identifier - OPTIONAL schema:Text The Order UUID of the Order, OrderQuote or OrderProposal, which is REQUIRED within the Orders feed.
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 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, independent of the Seller, that provides an application allowing Customers to make bookings. If brokerRole is set to https://openactive.io/NoBroker this property MUST NOT be included.
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 Array of schema:OrderItem The items that constitute the Order
schema:totalPaymentDue REQUIRED REQUIRED schema:PriceSpecification The total price of the Order, which includes or excludes tax depending on the taxMode.
oa:totalPaymentTax - REQUIRED Array of oa:TaxChargeSpecification Breakdown of tax payable for the Order. MUST NOT be included if taxCalculationExcluded is true. Due to differing approaches to tax arithmetic and rounding errors, the contents of this property may not be exactly equal to the sum of the unitTaxSpecification values of the OrderItems. The Broker MUST NOT attempt to perform any tax calculations, and MUST simply display these values as-is.
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 - - schema:URL The unique URL representing this version of the OrderProposal, or the version of the OrderProposal to which this Order is related. This URL MUST be constructed by appending /versions/{versionUUID} to the @id of the OrderProposal, where {versionUUID} is replaced by the OrderProposal Version UUID. This property MUST only be included in the OrderProposal responses from the Booking System and in the Orders feed, and in the Order request at B. It MUST NOT be included in OrderQuote requests or responses, or in Order responses.

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 REQUIRED only 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.
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. It does not include oa:orderRequiresApproval.

Property Broker Request Booking System Response Type Notes
@type REQUIRED REQUIRED schema:Text OrderProposal
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 - - - schema:Text
oa:orderCustomerNote OPTIONAL - - 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 allow for Booking Systems to cater for multiple Sellers.

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
@id REQUIRED REQUIRED URI A URI providing a unique identifier for the resource.
schema:identifier - OPTIONAL schema:Text The identifier of the Seller used by the Booking System.
schema:name - REQUIRED schema:Text Trading name of the Organization, or name of the Person. Note givenName and familyName MUST NOT be used for a Seller that is a Person.
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 - RECOMMENDED 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 of the Seller, for tax receipts and display during the booking journey.
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, which SHOULD be constructed by appending #/orderedItem/{orderItemId} to the @id of the Order, where {orderItemId} is replaced by the identifier of the OrderItem, which may be a string or an integer depending on the Booking System. This property is REQUIRED for B responses and MUST NOT be included for C1, C2 or P responses.
schema:position REQUIRED REQUIRED schema:Integer An integer representing the order of OrderItems within the array, as JSON-LD does not support array ordering. This allows the Broker to match any errors provided in the response to their request. The position of an OrderItem in the response MUST match the position for the same OrderItem provided in request, and implementations MUST use this value to match responses with requests instead of relying on the order of the elements in the array. This is REQUIRED for C1, C2, P and B requests and responses, but MUST NOT be persisted or be present in the Orders feed or other endpoints.
schema:orderItemStatus - REQUIRED schema:OrderItemStatus Either https://openactive.io/SellerCancelled, https://openactive.io/CustomerCancelled, https://openactive.io/OrderItemProposed, https://openactive.io/OrderItemConfirmed, 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. MUST NOT be included if taxCalculationExcluded is true.
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's requests and the Orders feed MUST only include the @type and @id within these objects; in these contexts, all other properties are ignored. See Opportunity data properties.
schema:additionalProperty - OPTIONAL 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:accessPass OPTIONAL 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 - OPTIONAL 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/OrderItemConfirmed or https://openactive.io/CustomerAttended
schema:attendee OPTIONAL OPTIONAL schema:Person The person attending the Opportunity related to the OrderItem.
oa:attendeeDetailsRequired OPTIONAL OPTIONAL Array of schema:Property The properties of schema:Person that are required for this OrderItem.
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 Customers, 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 Customers.
oa:ageRange - 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 oa:RequiredStatusType Indicates whether to accept this offer, a Customer 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. The default behaviour of this specification is https://openactive.io/Required.
oa:prepayment - OPTIONAL oa:RequiredStatusType Indicates if accepting this Offer requires a Customer to 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. The default behaviour of this specification is https://openactive.io/Unavailable for Offers with a price of 0, and https://openactive.io/Required otherwise. If an Offers has a price of 0 the value of this property MUST be either https://openactive.io/Unavailable or unspecified.
oa:availableChannel - - Array of oa:AvailableChannelType The channels through which a booking can be made. MUST be included in Opportunity Data [RPDE] open feeds, but MUST NOT be included in other requests or responses. 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 - - 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. If any features of the specification covered by this property are required to book the Offer, the Booking System MUST include this property within Offers in its Opportunity Data [RPDE] open feeds. This property MUST NOT be included in the request or response of any of the endpoints defined within this specification.

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, but does not include the properties availableChannel, advanceBooking, prepayment, ageRange, validFromBeforeStartDate, latestCancellationBeforeStartDate, and openBookingFlowRequirement.

Property Broker Request Booking System Response Type Notes
@type REQUIRED REQUIRED schema:Text OfferOverride
@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 Customers, 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 Customers.

10.1.9 schema:Person for customer or attendee

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. When used in the attendee property, required fields depend entirely on the attendeeDetailsRequired property.

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

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.

The identifier property MAY be provided by the Broker if a unique identifier for the Customer or attendee is available, to allow the Booking System to provide analytics based on repeat visits of the same guest. However such an identifier MUST only be matched within each unique set of Authentication Credentials to prevent identifier collisions across Booking Partners.

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.
oa:prepayment - OPTIONAL oa:RequiredStatusType When used for totalPaymentDue, indicates if proceeding with booking requires a Customer to 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. The default behaviour of this specification is https://openactive.io/Unavailable when price is 0, and https://openactive.io/Required otherwise. If the price is 0 the value of this property MUST be either https://openactive.io/Unavailable or unspecified.

10.1.14 oa:TaxChargeSpecification

TaxChargeSpecification subclasses PriceSpecification, and so includes the same properties and a few additional ones, listed below; however it does not include prepayment.

TaxChargeSpecification properties are intended to be rendered to the Customer as-is, and are not designed to be manipulated by the Broker. The Broker MUST NOT attempt to perform any tax calculations.

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
@type - REQUIRED schema:Text DynamicPayment
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 - REQUIRED 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 Whether the Customer MUST provide explicit consent to these terms before the booking is confirmed.
schema:dateModified - OPTIONAL schema:DateTime The date and time at which the webpage containing the contents of the terms, located at the url, was last updated. This property is RECOMMENDED when requiresExplicitConsent is true.

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

This class has been modelled closely on [RFC7807].

Property Status Type Notes
@type REQUIRED schema:Text OpenBookingError
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 A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced.
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: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 the name property is not supplied within the schema:Organisation object describing the Broker; or if the broker property supplied is not a valid schema:Organisation object.
UnnecessaryPaymentDetailsError 400 If the payment property of the Order is provided when it is expected to be absent.
MissingPaymentDetailsError 400 If the payment property of the Order is absent when it is expected to be provided.
IncompletePaymentDetailsError 400 If the payment property of the Order does not include identifier.
InvalidPaymentDetailsError 400 If the payment property of the OrderQuote or Order contains data that is not accepted by the Booking System for reconciliation, e.g. an invalid 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 Order 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 Order UUID clash.
OrderProposalVersionOutdatedError 500 If B fails due to a newer version of the OrderProposal existing than that specified by the Broker in orderProposalVersion.
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.
OpportunityOfferPairNotBookableError 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 Versioning Policy

The broad goal is to ensure that future versions of this specification remain backwards compatible. This will ensure that existing implementations remain valid.

New types of resource, relationships and properties can easily be added to extend the model without impacting existing functionality or interfaces. Potential breaking changes would include changing the definitions of existing properties, removing them from the specification, or changing the definition of existing interfaces.

To avoid this, the goal will be to:

11.1.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:

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

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",
      "position": 0,
      "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.3.1 Globally unique identifiers

@id properties are used as identifiers throughout this specification, for compatibility with [JSON-LD]. The value of such a property MUST always be an absolute URI that provides a stable globally unique identifier for the resource, as described in [RFC3986].

The primary purpose of the URI format in this context is to provide natural namespacing for the identifier. Hence, the URI itself MAY not resolve to a valid endpoint (unless otherwise specified), but MUST use a domain name controlled by the resource owner. In all cases where @id is referenced in this specification, the resource owner is the Booking System.

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 multiple Brokers are uniquely and securely provisioned to the authenticating party by the Booking System.

The term Booking Partner is used to refer to the authenticating party represented by the Authentication Credentials, which is distinct from the Broker represented by the broker property. The Booking Partner may be a Broker, or a Middleware, or any other authenticating party (including the Seller if brokerRole is set to NoBroker).

Authentication Credentials also grant granular authorisation for the specific Booking Partner to access all endpoints on behalf of explicitly consenting Sellers; with the exception of the Orders feed, which does not require granular authorisation. Access to a single Orders feed containing data items relating to all authorised Sellers of the Booking Partner MUST be available via their Authentication Credentials, and such data items MUST be marked as [RPDE] "state": "deleted" when such authorisation is revoked.

Authentication Credentials MUST be capable of being reprovisioned (e.g. a new [OAuth2] Client Secret generated) without losing any association to their related Order data.

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; and only do so if the associated Seller's authorisation for that Booking Partner is still active.

For security, Order UUIDs MUST only be considered unique within a given set of Authentication Credentials. It MUST be possible for two different Orders that are created via two different Authentication Credentials to exist with the same Order UUID.

This specification does not mandate a particular authentication method, but its recommendation is that implementers should consider using [OAuth2] as it is well-defined, widely supported and can be used in a variety of different application flows (e.g. via a JavaScript web application or between servers). For Booking Systems that support multiple Sellers, OpenID Connect ([OpenIdConnect]) is recommended in addition to this for the Booking Partner to access a particular Seller.

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

This specification permits trusted Middleware to use a single set of Authentication Credentials to interact with the Booking System for their authorised Sellers on behalf of multiple Brokers. 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.

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

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 the Middleware or any other Booking Partner 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 Booking Partners via their unique Authentication Credentials, as well as against Brokers identified by the broker property, 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 the Customer.

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.

11.7.4 OpenID Connect Booking Partner Authentication for Multiple Seller Systems

For Booking Systems that support multiple Sellers, OpenID Connect ([OpenIdConnect]) is recommended when providing the Booking Partner access to a particular Seller.

In order to support convergence amongst implementations, a number of recommendations are included below, which include recommended terms for names of Scopes and Claims.

This section requires a basic understanding of OpenID Connect.

11.7.4.1 OAuth Flows and Scopes

The objective of an OAuth flow is for the Booking Partner to acquire an Access Token, which is an Authentication Credential that provides access to a subset the Open Booking API endpoints of the Booking System for a particular Seller, or to the Orders feed for all authorised Sellers.

The Booking Partner SHOULD include the relevant Access Token in the Authorization header when accessing the Open Booking API endpoints.

An expiry duration of 15 minutes is RECOMMENDED for Access Token, to ensure the Seller has sufficient control.

The recommended flows are as following:

An OAuth Scope defines access to a set of endpoints, and also expectations about Claims returned. An Access Token can be requested that includes a number of scopes, via the relevant OAuth flow.

The RECOMMENDED Scopes and flows are defined in the table below:

Scope Endpoints OAuth flow used to acquire Access Token
openactive-openbooking

OrderQuote Creation (C1)

OrderQuote Creation (C2)

OrderQuote Deletion

Order Creation (B)

Order Deletion

Order Cancellation

Order Status

Authorization Code Flow

openactive-ordersfeed Orders RPDE Feed Client Credentials Flow

In order to implement functionality to suspend bookings temporarily with a particular Seller, the Booking System SHOULD temporarily revoke access to the openactive-openbooking scope. The Seller's items SHOULD still be available in the Orders Feed, and hence Customers are still able to receive refunds and notifications, but cannot make new bookings or initiate cancellations.

11.7.4.1.1 Authorization Code Flow

To access any of the endpoints defined in this specification other than the Orders RPDE Feed, the Booking Partner SHOULD first acquire a valid Access Token with an openactive-openbooking Scope, by having a particular Seller complete the Authorization Code Flow.

This flow presents the Seller with an authentication and consent prompt, that they will be familiar from websites that offer "Login with my social media account" functionality:

Seller Authentication Page
Figure 19 Example authorization page for a Booking Partner, presented by a Booking System.

A Refresh Token SHOULD also be also provided during this flow, which allows the Booking Partner to request another Access Token once it has expired, without the Seller needing to reauthenticate.

Additionally, a "one-time usage" ID Token SHOULD be provided during this flow which contains the SellerId and other details of the Seller. This allows the Booking Partner to store the Access Token and Refresh Token against the correct SellerId in their database, so they can use these when booking the Seller's opportunities.

The Authorization Request of this flow SHOULD include the openactive-openbooking, openid, and offline_access Scopes, to ensure that an Access Token, ID Token and Refresh Token are all returned for the authenticating Seller.

For this flow, it is RECOMMENDED that the OpenID Connect subject is not the end user who is following the OAuth flow, but is instead the Seller that they represent - such that if, for example, the end user no longer works for the Seller and deletes their account, their authentication grants remain unaffected. This recommendation conforms with OpenID Connect from a technical perspective, which is useful when utilising existing libraries.

Authorization Code Flow
Figure 20 Authorization Code Flow
11.7.4.1.2 Client Credentials Flow

The straightforward Client Credentials Flow SHOULD be used to retrieve an Access Token with an openactive-ordersfeed Scope, which grants access to the Orders Feed endpoint as above.

The Authorization Request of the flow SHOULD include only the openactive-ordersfeed Scope.

Client Credentials Flow
Figure 21 Client Credentials Flow
11.7.4.2 Custom Claims

Claims are simply key-value pairs that are included in Access Tokens and ID Tokens; each Claim is "claiming" a fact about the subject (in this case, the Seller). For example a token may include a "claim" that the Seller has a name of "Acme Leisure".

The OpenActive namespace includes a number of terms for use as Custom Claims.

11.7.4.2.1 ID Token claims

The ID Token is designed to be read by the Booking Partner to give them information about the Seller that has just authenticated. This allows the Booking Partner to store the Access Token and Refresh Token against the correct SellerId in their database, so they can use these when booking the Seller's opportunities.

It is RECOMMENDED that the openactive-openbooking Scope includes an implicit request that Claims listed below are included in the ID Token.

The following custom claims are for use by the Booking Partner, and must conform to the custom claim names specified below. The custom claim names are collision-resistant in accordance with the OIDC specification.

Custom claim Description Exactly matches
https://openactive.io/sellerName The seller name. name of seller
https://openactive.io/sellerLogo A URL of the logo of the Seller. logo of seller
https://openactive.io/sellerUrl The URL of the website of the Seller. url of seller
https://openactive.io/sellerId The Seller ID as a JSON-LD ID. Also allows for compatibility with existing authentication implementations which might be using "sub" to include a different identifier. Booking partners will use this to determine which Seller ID the provided accessToken is intended for. id of seller
https://openactive.io/bookingServiceName The name of the Booking System name of bookingService
https://openactive.io/bookingServiceUrl The url of the website of the Booking System url of bookingService
11.7.4.2.2 Access Token claims

To help simplify the implementation, it is recommended that Access Tokens include the following custom claims.

The Access Token is only read internally by the Booking System, and so these claims are simply a recommendation. Hence the claim names do not need to be standardised as long as they are internally consistent.

Custom claim Description Scopes
https://openactive.io/clientId The Booking Partner Client ID that requested the Access Token. Note that this claim is due to be featured in a future OAuth 2.0 specification, and it is RECOMMENDED that the newly specified claim is used in preference to this one, once ratified.

openactive-openbooking and openactive-ordersfeed

https://openactive.io/sellerId Seller ID as a JSON-LD ID, which is useful to be provided to Open Booking API endpoints to determine which Seller the Access Token is intended for. It is consistent with the claim name used in the ID Token. openactive-openbooking

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
[RFC3986]
Uniform Resource Identifier (URI): Generic Syntax. T. Berners-Lee; R. Fielding; L. Masinter. IETF. January 2005. Internet Standard. URL: https://tools.ietf.org/html/rfc3986
[RFC4122]
A Universally Unique IDentifier (UUID) URN Namespace. P. Leach; M. Mealling; R. Salz. IETF. July 2005. Proposed Standard. URL: https://tools.ietf.org/html/rfc4122
[RFC7807]
Problem Details for HTTP APIs. M. Nottingham; E. Wilde. IETF. March 2016. Proposed Standard. URL: https://tools.ietf.org/html/rfc7807
[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/