Create a new member with an associated booking

POST /api/bookings/add-member-with-booking

Success: 201 Created

Overview

Registers a brand new member and confirms their first booking in one call. It is the endpoint behind "book now, no account needed": the person is unknown to the system when the call starts and has a member record and a confirmed booking when it finishes.

You still lease and price the slot first. The normal order is: hold the slot with Create or update a booking lease, price it with Retrieve pricing information for different members, then send the resulting leaseId and signed pricingQuote here alongside the person's details. Leases expire ten minutes after creation and a pricing quote is valid for one hour, so this is a time-limited flow.

The work happens in a fixed order, and each step can fail the whole call:

  1. Validate — email rules, address and telephone primary rules, and a check that the member does not already exist. Failures here are 400 and nothing is created.
  2. Create the guardian, if one was sent, then the member; then apply addresses, contact details, signed agreements, marketing consent, user fields and the guardian link.
  3. Create the bookings against the new member.
  4. Confirm both members.

Age of consent drives the shape of the request. If the member's dateOfBirth puts them below your system's age of consent they are a junior: guardianProfile is then required, and sending one for anybody else is rejected. Below the age of an online account a junior must not have an email address; everyone else must have one.

Rollback is best-effort, not all-or-nothing. If booking creation fails, the endpoint deletes the member and guardian it has just created — but that clean-up can itself fail, in which case the member exists without a booking and has to be tidied up by hand. Treat a 422 as "check before retrying" rather than "nothing happened".

On success the endpoint returns 201 Created with the new member id, the guardian id where there is one, and the created bookings.

Endpoint

POST {base_url}/api/bookings/add-member-with-booking

{base_url} is the environment URL issued to your integration. See Authenticating with Gladstone APIs.

Request body

FieldTypeRequiredDescription
memberProfileobjectYesThe person the booking is for and the account being created. Always required. Their dateOfBirth decides whether a guardianProfile is required and whether they may have an email address.
memberProfile.titleIdstringNoTitle ID cannot be more than 15 characters.
memberProfile.firstNamestringYesFirst name cannot be more than 25 characters.
memberProfile.lastNamestringYesLast name cannot be more than 25 characters.
memberProfile.dateOfBirthstring (date-time)NoDate of Birth cannot be in the future.
memberProfile.siteIdstringYesSiteId cannot be more than 15 characters.
memberProfile.memberLanguagestringNoPassed straight through to member creation as the member's preferred language.
memberProfile.pendingConfirmationbooleanNoPassed through to member creation to mark the record as awaiting confirmation. It rarely changes the end state: once the bookings have been created the endpoint confirms both the member and the guardian anyway. That confirmation step is best-effort — a failure is logged and the call still returns 201.
guardianProfileobjectNoThe responsible adult for a junior member, created as a member in their own right, linked to the new member and set as the paying member on every booking. Required when memberProfile.dateOfBirth puts the member below your system's age of consent — omit it and the request fails with A guardian must be specified for this junior member. Sending one for anybody else fails with A guardian cannot be specified for this non-junior member. The guardian is created first, so if their details are rejected the member is never created at all. A guardian must always have a valid email address, whatever the junior's own email rules are.
guardianProfile.addressDetailsobjectNoThe guardian's address, if you are capturing one. Optional as a whole — omit it and the member is created without an address — but if you send it, address1 is required and exactly one of the two addresses must be marked primary.
guardianProfile.addressDetails.address1objectYesThe first address. Required whenever the containing object is sent.
guardianProfile.addressDetails.address1.addressLine1stringYesFirst line of the address — house number and street. Required.
guardianProfile.addressDetails.address1.addressLine2stringNoSecond address line, for a locality or building name. Send it empty or leave it out when there is nothing to put in it.
guardianProfile.addressDetails.address1.addressLine3stringNoThird address line, used the same way as addressLine2 when an address needs it.
guardianProfile.addressDetails.address1.townCitystringYesTown or city. Required.
guardianProfile.addressDetails.address1.regionstringNoCounty, state or region. Optional; nothing else depends on it.
guardianProfile.addressDetails.address1.postalCodestringYesPostcode. Required.
guardianProfile.addressDetails.address1.countrystringNoCountry. Optional.
guardianProfile.addressDetails.address1.isPrimarybooleanYesMarks this as the main address for correspondence. Exactly one address must be primary: if neither is, the request fails with One address must be primary; if both are, with Only one address can be primary. With only address1 sent, set it to true.
guardianProfile.addressDetails.address2objectNoA second address, such as a term-time or correspondence address. Optional; omit it entirely if there is only one.
guardianProfile.addressDetails.address2.addressLine1stringYesFirst line of the second address. Required whenever address2 is sent.
guardianProfile.addressDetails.address2.addressLine2stringNoSecond line of the second address; optional, as on address1.
guardianProfile.addressDetails.address2.addressLine3stringNoThird line of the second address; optional, as on address1.
guardianProfile.addressDetails.address2.townCitystringYesTown or city of the second address. Required whenever address2 is sent.
guardianProfile.addressDetails.address2.regionstringNoCounty, state or region of the second address. Optional.
guardianProfile.addressDetails.address2.postalCodestringYesPostcode of the second address. Required whenever address2 is sent.
guardianProfile.addressDetails.address2.countrystringNoCountry of the second address. Optional.
guardianProfile.addressDetails.address2.isPrimarybooleanYesMarks the second address as the main one. Set it only when address1.isPrimary is false, since exactly one of the two must be primary — see address1.isPrimary for the two failure messages.
guardianProfile.contactDetailsobjectYesThe guardian's email addresses and telephone numbers. The object itself is always required, even when every field inside it is empty — a junior below the age of an online account, for instance, sends it with no email at all.
guardianProfile.contactDetails.email1objectNoThe primary email address. The address stored on the member is taken from here unless email2 is marked primary and has a value, in which case that one wins.
guardianProfile.contactDetails.email1.emailAddressstringNoAn email address cannot be more than 255 characters.
guardianProfile.contactDetails.email1.isPrimarybooleanNoHas no effect on which address is stored: email1 is used by default whether or not this is set. It is email2.isPrimary that changes the outcome.
guardianProfile.contactDetails.email2objectNoA second email address. Only used if isPrimary is true and emailAddress is not empty, in which case it replaces email1 as the member's stored address.
guardianProfile.contactDetails.email2.emailAddressstringNoAn email address cannot be more than 255 characters.
guardianProfile.contactDetails.email2.isPrimarybooleanNoSet this to true, with a non-empty emailAddress, to store email2 instead of email1. Left false, the second address is ignored.
guardianProfile.contactDetails.telephone1objectNoA telephone number for the member. Optional, but if you send any of telephone1, telephone2 or mobile, exactly one of them must be marked primary.
guardianProfile.contactDetails.telephone1.numberstringNoA phone number cannot be more than 50 characters.
guardianProfile.contactDetails.telephone1.isPrimarybooleanNoMarks this as the main contact number. Across telephone1, telephone2 and mobile, exactly one must be primary once any of them is sent: none fails with One contact number must be primary, more than one with Only one contact number can be primary.
guardianProfile.contactDetails.telephone2objectNoA second telephone number. Optional, and subject to the same single-primary rule as telephone1.
guardianProfile.contactDetails.telephone2.numberstringNoA phone number cannot be more than 50 characters.
guardianProfile.contactDetails.telephone2.isPrimarybooleanNoMarks the second number as the main one; see telephone1.isPrimary for the rule and its two failure messages.
guardianProfile.contactDetails.mobileobjectNoA mobile number. Optional, and counted alongside the two telephone numbers when the single-primary rule is applied.
guardianProfile.contactDetails.mobile.numberstringNoA phone number cannot be more than 50 characters.
guardianProfile.contactDetails.mobile.isPrimarybooleanNoMarks the mobile as the main contact number; see telephone1.isPrimary for the rule and its two failure messages.
guardianProfile.titleIdstringNoTitle ID cannot be more than 15 characters.
guardianProfile.firstNamestringYesFirst name cannot be more than 25 characters.
guardianProfile.lastNamestringYesLast name cannot be more than 25 characters.
guardianProfile.dateOfBirthstring (date-time)NoDate of Birth cannot be in the future.
guardianProfile.siteIdstringYesSiteId cannot be more than 15 characters.
guardianProfile.memberLanguagestringNoPassed straight through to member creation as the member's preferred language.
guardianProfile.pendingConfirmationbooleanNoPassed through to member creation to mark the record as awaiting confirmation. It rarely changes the end state: once the bookings have been created the endpoint confirms both the member and the guardian anyway. That confirmation step is best-effort — a failure is logged and the call still returns 201.
addressDetailsobjectNoThe member's address, if you are capturing one. Optional as a whole — omit it and the member is created without an address — but if you send it, address1 is required and exactly one of the two addresses must be marked primary.
addressDetails.address1objectYesThe first address. Required whenever the containing object is sent.
addressDetails.address1.addressLine1stringYesFirst line of the address — house number and street. Required.
addressDetails.address1.addressLine2stringNoSecond address line, for a locality or building name. Send it empty or leave it out when there is nothing to put in it.
addressDetails.address1.addressLine3stringNoThird address line, used the same way as addressLine2 when an address needs it.
addressDetails.address1.townCitystringYesTown or city. Required.
addressDetails.address1.regionstringNoCounty, state or region. Optional; nothing else depends on it.
addressDetails.address1.postalCodestringYesPostcode. Required.
addressDetails.address1.countrystringNoCountry. Optional.
addressDetails.address1.isPrimarybooleanYesMarks this as the main address for correspondence. Exactly one address must be primary: if neither is, the request fails with One address must be primary; if both are, with Only one address can be primary. With only address1 sent, set it to true.
addressDetails.address2objectNoA second address, such as a term-time or correspondence address. Optional; omit it entirely if there is only one.
addressDetails.address2.addressLine1stringYesFirst line of the second address. Required whenever address2 is sent.
addressDetails.address2.addressLine2stringNoSecond line of the second address; optional, as on address1.
addressDetails.address2.addressLine3stringNoThird line of the second address; optional, as on address1.
addressDetails.address2.townCitystringYesTown or city of the second address. Required whenever address2 is sent.
addressDetails.address2.regionstringNoCounty, state or region of the second address. Optional.
addressDetails.address2.postalCodestringYesPostcode of the second address. Required whenever address2 is sent.
addressDetails.address2.countrystringNoCountry of the second address. Optional.
addressDetails.address2.isPrimarybooleanYesMarks the second address as the main one. Set it only when address1.isPrimary is false, since exactly one of the two must be primary — see address1.isPrimary for the two failure messages.
contactDetailsobjectYesThe member's email addresses and telephone numbers. The object itself is always required, even when every field inside it is empty — a junior below the age of an online account, for instance, sends it with no email at all.
contactDetails.email1objectNoThe primary email address. The address stored on the member is taken from here unless email2 is marked primary and has a value, in which case that one wins.
contactDetails.email1.emailAddressstringNoAn email address cannot be more than 255 characters.
contactDetails.email1.isPrimarybooleanNoHas no effect on which address is stored: email1 is used by default whether or not this is set. It is email2.isPrimary that changes the outcome.
contactDetails.email2objectNoA second email address. Only used if isPrimary is true and emailAddress is not empty, in which case it replaces email1 as the member's stored address.
contactDetails.email2.emailAddressstringNoAn email address cannot be more than 255 characters.
contactDetails.email2.isPrimarybooleanNoSet this to true, with a non-empty emailAddress, to store email2 instead of email1. Left false, the second address is ignored.
contactDetails.telephone1objectNoA telephone number for the member. Optional, but if you send any of telephone1, telephone2 or mobile, exactly one of them must be marked primary.
contactDetails.telephone1.numberstringNoA phone number cannot be more than 50 characters.
contactDetails.telephone1.isPrimarybooleanNoMarks this as the main contact number. Across telephone1, telephone2 and mobile, exactly one must be primary once any of them is sent: none fails with One contact number must be primary, more than one with Only one contact number can be primary.
contactDetails.telephone2objectNoA second telephone number. Optional, and subject to the same single-primary rule as telephone1.
contactDetails.telephone2.numberstringNoA phone number cannot be more than 50 characters.
contactDetails.telephone2.isPrimarybooleanNoMarks the second number as the main one; see telephone1.isPrimary for the rule and its two failure messages.
contactDetails.mobileobjectNoA mobile number. Optional, and counted alongside the two telephone numbers when the single-primary rule is applied.
contactDetails.mobile.numberstringNoA phone number cannot be more than 50 characters.
contactDetails.mobile.isPrimarybooleanNoMarks the mobile as the main contact number; see telephone1.isPrimary for the rule and its two failure messages.
optInMarketingbooleanNoWhether the member consents to marketing. true creates a default communication channel consent and registers them against the default campaign; false, or omitted, does nothing at all — no consent record is written either way, so a later opt-in is a separate call. If the consent calls fail, the whole request fails with 422 and the member is rolled back.
signedAgreementsobjectNoWhich agreements the person has accepted at the point of booking. Each flag that is true is stamped with the current UTC time as the signed date; each that is false, or the whole object omitted, leaves that agreement unsigned. You cannot backdate a signature — the timestamp is always the moment of the call. Applies to the member only; agreements are not recorded for the guardian.
signedAgreements.privacyPolicybooleanNoSet true when the person has accepted the privacy policy, which records the acceptance date as now. false or omitted leaves it unrecorded.
signedAgreements.termsAndConditionsbooleanNoSet true when the person has accepted the terms and conditions, recording the acceptance date as now. false or omitted leaves it unrecorded.
signedAgreements.healthCommitmentStatementbooleanNoSet true when the person has signed the health commitment statement, recording the date as now. false or omitted leaves it unrecorded — worth checking against your operator's requirements for the activity being booked.
userFieldsarray of objectNoValues for the operator's custom member fields — the extra questions a site asks on sign-up. Omit it, or send an empty array, and no user fields are written. Sent as one batch after the member is created; if the batch is rejected, the whole request fails with 422 and the member is rolled back.
userFields[].userFieldIdinteger (int32)YesUser field ID cannot be empty.
userFields[].valuestringYesUser field value cannot be empty.
bookingDetailsarray of objectYesThe bookings to create for the new member. Required, and at least one entry is expected — this endpoint exists to create a member with a booking. All entries are sent to the bookings service together, so they succeed or fail as a set; a failure rolls the member back.
bookingDetails[].leaseIdstringYesThe lease holding the slot, as returned by Create or update a booking lease. Required. It is single-use: the lease is deleted once the booking is created. Send either this or bookingId, never both — the bookings service rejects a booking carrying both with Either LeaseID or BookingID must be specified but not both, and one carrying neither with Either LeaseID or BookingID must be specified.
bookingDetails[].bookingIdinteger (int32)NoThe id of an existing booking to join, used instead of a lease when adding the member to a class or group booking that already exists. Mutually exclusive with leaseId; see that field for the two rejection messages.
bookingDetails[].pricingQuoteobjectYesThe signed price quotation for this slot, taken from Retrieve pricing information for different members. Required. Pass the object through exactly as pricing returned it — every field below is covered by signature, so changing so much as a rounding fails the check with The pricing quote is invalid. Quotes are short-lived: one whose timeStamp is more than an hour old is rejected with The time frame where the request is considered valid has expired., and one dated more than ten minutes ahead with The time stamp of the request can not be in the future.
bookingDetails[].pricingQuote.totalPriceobjectNoThe total payable for the slot, as quoted. Part of the signed quote — echo it back unchanged.
bookingDetails[].pricingQuote.totalPrice.amountnumber (double)NoThe quoted total as a decimal. Echo it back exactly as received; re-serialising it with a different precision breaks the signature.
bookingDetails[].pricingQuote.totalPrice.currencystringNoThe currency the quote is expressed in. Echo it back unchanged.
bookingDetails[].pricingQuote.breakdownobjectNoHow the total splits across the people on the booking. Part of the signed quote; send it back untouched even if you do not display it.
bookingDetails[].pricingQuote.breakdown.memberPriceobjectNoThe portion of the quote covering the member's own place, as opposed to any additional bookees.
bookingDetails[].pricingQuote.breakdown.memberPrice.priceLevelIdstringNoThe price level the member's place was quoted at — what determines a concession or off-peak rate. Echo it back unchanged.
bookingDetails[].pricingQuote.breakdown.memberPrice.totalPricenumber (double)NoThe total quoted for the member's own place at that price level.
bookingDetails[].pricingQuote.breakdown.memberPrice.productPricesarray of objectNoThe individual products making up the member's place — the activity itself plus any hire or extras priced with it.
bookingDetails[].pricingQuote.breakdown.memberPrice.productPrices[].idstringNoThe product being charged for. Echo it back unchanged.
bookingDetails[].pricingQuote.breakdown.memberPrice.productPrices[].quantityinteger (int32)NoHow many of that product the quote covers.
bookingDetails[].pricingQuote.breakdown.memberPrice.productPrices[].timeBandIdstringNoThe time band the product was priced in, which is how peak and off-peak rates differ on the same product.
bookingDetails[].pricingQuote.breakdown.memberPrice.productPrices[].amountnumber (double)NoThe amount quoted for that product line.
bookingDetails[].pricingQuote.breakdown.memberPrice.productPrices[].taxCodeIdstringNoThe tax code applied to that product line.
bookingDetails[].pricingQuote.breakdown.memberPrice.productPrices[].taxRatenumber (double)NoThe tax rate that the tax code resolved to when the quote was produced.
bookingDetails[].pricingQuote.breakdown.currencystringNoThe currency the breakdown is expressed in, matching totalPrice.currency.
bookingDetails[].pricingQuote.breakdown.priceLevelsarray of objectNoThe additional bookees on the slot, grouped by price level — populated for party bookings, empty for an ordinary one.
bookingDetails[].pricingQuote.breakdown.priceLevels[].countinteger (int32)NoHow many additional bookees were quoted at this price level.
bookingDetails[].pricingQuote.breakdown.priceLevels[].pricePerAdditionalBookeenumber (double)NoThe per-person price quoted at this price level.
bookingDetails[].pricingQuote.breakdown.priceLevels[].idstringNoThe price level these additional bookees were quoted at.
bookingDetails[].pricingQuote.breakdown.priceLevels[].totalPricenumber (double)NoThe total quoted for all the bookees at this price level — count multiplied by the per-person price, as calculated by pricing.
bookingDetails[].pricingQuote.breakdown.priceLevels[].productPricesarray of objectNoThe individual products making up the price for this group of additional bookees.
bookingDetails[].pricingQuote.breakdown.priceLevels[].productPrices[].idstringNoThe product being charged for in this group's line.
bookingDetails[].pricingQuote.breakdown.priceLevels[].productPrices[].quantityinteger (int32)NoHow many of that product this group's quote covers.
bookingDetails[].pricingQuote.breakdown.priceLevels[].productPrices[].timeBandIdstringNoThe time band this group's product line was priced in.
bookingDetails[].pricingQuote.breakdown.priceLevels[].productPrices[].amountnumber (double)NoThe amount quoted for this group's product line.
bookingDetails[].pricingQuote.breakdown.priceLevels[].productPrices[].taxCodeIdstringNoThe tax code applied to this group's product line.
bookingDetails[].pricingQuote.breakdown.priceLevels[].productPrices[].taxRatenumber (double)NoThe tax rate that tax code resolved to for this group's product line.
bookingDetails[].pricingQuote.timeStampstringNoWhen the quote was issued, and what makes it expire. The booking is rejected if this is more than one hour in the past or more than ten minutes in the future, so re-price rather than reusing an old quote. Echo it back exactly as issued — it is covered by signature.
bookingDetails[].pricingQuote.signaturestringNoThe signature the pricing service put on the quote, recomputed and compared when the booking is created. Any change anywhere in pricingQuote invalidates it and the booking is rejected with The pricing quote is invalid. Treat the whole quote as opaque.
bookingDetails[].memberIdinteger (int32)NoIgnored. The endpoint overwrites it with the id of the member it has just created — which it has to, because that member did not exist when you built the request.
bookingDetails[].payingMemberIdinteger (int32)NoIgnored. The endpoint overwrites it with the guardian's id, or 0 when there is no guardian, so a junior's booking is always charged to their guardian.

Sample request

cURL
curl -X POST '{base_url}/api/bookings/add-member-with-booking' \
  -H 'Authorization: Bearer {accessToken}' \
  -H 'X-Use-SSO: true' \
  -H 'Content-Type: application/json' \
  -d '{
  "memberProfile": {
    "titleId": "string",
    "firstName": "string",
    "lastName": "string",
    "dateOfBirth": "2026-08-14T19:00:00Z",
    "siteId": "string",
    "memberLanguage": "string",
    "pendingConfirmation": true
  },
  "guardianProfile": {
    "addressDetails": {
      "address1": {
        "addressLine1": "string",
        "addressLine2": "string",
        "addressLine3": "string",
        "townCity": "string",
        "region": "string",
        "postalCode": "string",
        "country": "string",
        "isPrimary": true
      },
      "address2": {
        "addressLine1": "string",
        "addressLine2": "string",
        "addressLine3": "string",
        "townCity": "string",
        "region": "string",
        "postalCode": "string",
        "country": "string",
        "isPrimary": true
      }
    },
    "contactDetails": {
      "email1": {
        "emailAddress": "member@example.com",
        "isPrimary": true
      },
      "email2": {
        "emailAddress": "member@example.com",
        "isPrimary": true
      },
      "telephone1": {
        "number": "string",
        "isPrimary": true
      },
      "telephone2": {
        "number": "string",
        "isPrimary": true
      },
      "mobile": {
        "number": "string",
        "isPrimary": true
      }
    },
    "titleId": "string",
    "firstName": "string",
    "lastName": "string",
    "dateOfBirth": "2026-08-14T19:00:00Z",
    "siteId": "string",
    "memberLanguage": "string",
    "pendingConfirmation": true
  },
  "addressDetails": {
    "address1": {
      "addressLine1": "string",
      "addressLine2": "string",
      "addressLine3": "string",
      "townCity": "string",
      "region": "string",
      "postalCode": "string",
      "country": "string",
      "isPrimary": true
    },
    "address2": {
      "addressLine1": "string",
      "addressLine2": "string",
      "addressLine3": "string",
      "townCity": "string",
      "region": "string",
      "postalCode": "string",
      "country": "string",
      "isPrimary": true
    }
  },
  "contactDetails": {
    "email1": {
      "emailAddress": "member@example.com",
      "isPrimary": true
    },
    "email2": {
      "emailAddress": "member@example.com",
      "isPrimary": true
    },
    "telephone1": {
      "number": "string",
      "isPrimary": true
    },
    "telephone2": {
      "number": "string",
      "isPrimary": true
    },
    "mobile": {
      "number": "string",
      "isPrimary": true
    }
  },
  "optInMarketing": true,
  "signedAgreements": {
    "privacyPolicy": true,
    "termsAndConditions": true,
    "healthCommitmentStatement": true
  },
  "userFields": [
    {
      "userFieldId": 12345
    }
  ],
  "bookingDetails": [
    {
      "leaseId": "string",
      "bookingId": 12345,
      "pricingQuote": {
        "totalPrice": {
          "amount": 10.5,
          "currency": "GBP"
        },
        "breakdown": {
          "memberPrice": {
            "priceLevelId": "string",
            "totalPrice": 10.5,
            "productPrices": [
              {
                "id": "string",
                "quantity": 1,
                "timeBandId": "string",
                "amount": 10.5,
                "taxCodeId": "string",
                "taxRate": 10.5
              }
            ]
          },
          "currency": "GBP",
          "priceLevels": [
            {
              "count": 1,
              "pricePerAdditionalBookee": 10.5,
              "id": "string",
              "totalPrice": 10.5,
              "productPrices": [
                {
                  "id": "string",
                  "quantity": 1,
                  "timeBandId": "string",
                  "amount": 10.5,
                  "taxCodeId": "string",
                  "taxRate": 10.5
                }
              ]
            }
          ]
        },
        "timeStamp": "string",
        "signature": "string"
      },
      "memberId": 12345,
      "payingMemberId": 12345
    }
  ]
}'

Response fields

FieldTypeDescription
memberIdinteger (int32)The id of the newly created member — the value to use everywhere else in the API from now on. Always populated on a 201.
guardianMemberIdinteger (int32)The id of the guardian created alongside the member, or 0 when no guardianProfile was sent. The two records are linked, and this member is the one charged for the bookings.
bookingsarray of objectOne entry per booking created, in the order they were requested. Passed straight through from the bookings service.
bookings[].leaseIDstringThe lease this booking was created from, echoed back so you can match the entry to the request you sent. The lease itself is deleted once the booking exists, so the value is a correlation key, not something you can reuse.
bookings[].slotReferencestringThe slot the booking was made against, in the same opaque form availability and leasing use. Use it to match this booking to an entry in discountDetails.
bookings[].bookingIDinteger (int32)The booking's id — the value to pass to cancellation and attendance endpoints later. Keep it.
bookings[].bookingRefinteger (int32)The member-facing booking reference for this member's place, distinct from bookingID: several members can share one booking id but each has their own reference.
bookings[].salesInvoiceIdsarray of integerThe sales invoices raised for the booking. Empty for a zero-priced booking; more than one where the booking generated several invoices. These are what a payment is taken against.
bookings[].bookeePriceLevelsarray of objectThe price levels the places on this booking ended up at, with counts and prices — what to show on a confirmation screen. Reflects what was actually booked rather than what was quoted.
bookings[].bookeePriceLevels[].idstringThe price level's identifier.
bookings[].bookeePriceLevels[].descriptionstringThe price level's name as configured by the operator, for display. See translations for other languages.
bookings[].bookeePriceLevels[].countinteger (int32)How many places on the booking fell at this price level.
bookings[].bookeePriceLevels[].pricenumber (double)The price charged per place at this price level. Null where no price was resolved.
bookings[].bookeePriceLevels[].translationsarray of objectTranslated versions of the price level text, where the operator has configured them. Empty or absent when only the default language exists.
bookings[].bookeePriceLevels[].translations[].typestringWhich piece of text this translation is for.
bookings[].bookeePriceLevels[].translations[].culturestringThe culture the text is for, so you can pick the one matching your caller's locale.
bookings[].bookeePriceLevels[].translations[].textstringThe translated text itself, to display in place of description.
bookings[].keyBookeeBookingInfoobjectThe price level and price for the key bookee — the person the booking is held in the name of. On an ordinary booking that is the member, and this simply repeats their price level. On a party booking it is the party's key bookee, and it is null when the key bookee is not among the people in this booking.
bookings[].keyBookeeBookingInfo.priceLevelIdstringThe price level the key bookee's place was charged at.
bookings[].keyBookeeBookingInfo.pricenumber (double)The price charged for the key bookee's place.
discountDetailsarray of objectAny discounts applied while creating the bookings, listed per slot rather than per booking. Empty when nothing was discounted.
discountDetails[].slotReferencestringThe slot the discount applied to. Match it against bookings[].slotReference to attribute the discount to a booking.
discountDetails[].discountTypestringWhat kind of discount was applied.
discountDetails[].discountAmountnumber (double)How much was taken off, in the currency of the quote. Null where no amount was recorded.

Sample response

201 Created
{
  "memberId": 12345,
  "guardianMemberId": 12345,
  "bookings": [
    {
      "leaseID": "string",
      "slotReference": "string",
      "bookingID": 12345,
      "bookingRef": 1,
      "salesInvoiceIds": [
        12345
      ],
      "bookeePriceLevels": [
        {
          "id": "string",
          "description": "string",
          "count": 1,
          "price": 10.5,
          "translations": [
            {
              "type": "string",
              "culture": "string",
              "text": "string"
            }
          ]
        }
      ],
      "keyBookeeBookingInfo": {
        "priceLevelId": "string",
        "price": 10.5
      }
    }
  ],
  "discountDetails": [
    {
      "slotReference": "string",
      "discountType": "string",
      "discountAmount": 10.5
    }
  ]
}

Errors

StatusMeaningWhen
400Bad requestThe guardian does not match the member's age — A guardian must be specified for this junior member when one is below the age of consent and none was sent, or A guardian cannot be specified for this non-junior member when one was sent for an adult.
400Bad requestThe email does not match the age rules — One or more members are missing an email address. when one is required, or Email cannot be specified for a member that requires a guardian. for a junior below the age of an online account.
400Bad requestThe email address is malformed — The email address "{email}" is not a valid email address.
400Bad requestThe primary flags are wrong — One address must be primary, Only one address can be primary, One contact number must be primary, or Only one contact number can be primary.
400Bad requestThe person is already on the system — At least one of the members already exists in the database. The check matches on first name, last name and email address, and runs for the guardian as well; anyone without an email address — a junior below the age of an online account — is skipped and so is never caught by it. Look the member up and book against the existing record instead.
400Bad requestA field breaks its length or format rule, such as First name cannot be more than 25 characters or Date of Birth cannot be in the future. Nothing is created.
422Unprocessable entityThe bookings could not be created — The bookings could not be created: {message}, where {message} describes the booking failure and covers an expired or already-used lease, an invalid pricing quote, and a slot that filled up in the meantime. The member and guardian are then deleted, but that clean-up is best-effort: verify before retrying.
422Unprocessable entityThe member or guardian could not be created — The member could not be created. {message} or The guardian member could not be created. {message}, where {message} gives the reason member creation was rejected.
422Unprocessable entityA follow-up step on the new member failed — the address, contact details, consent details, marketing preferences, user fields or the guardian link. The member created so far is deleted.
500Internal server errorAn unexpected server-side failure, or an internal step failing in a way the endpoint does not translate into a more specific status. The body is empty; state may be partially created.
503Service unavailablePart of the platform this call depends on could not be reached — At least one of the services is unavailable. The call is not all-or-nothing, so a failure part-way through can leave a member without a booking; check before retrying.

This page as markdown · All pages · llms.txt