# Retrieve pricing information for different members

`POST /api/pricing/slots`

- **Success:** `200 OK`
- **Products:** Operator (`FOH`), Consumer (`CONSUMER`) (any one of these products, not all)
- **Features:** Online Bookings (`ONLINEBOOK`), Online Joining (`ONLINEJOIN`), Front of House Generic Access (`FOHGENERIC`) (any one of these features, not all)

HTML version: [Retrieve pricing information for different members](https://support.gladstonesoftware.cloud/reference/bookings/retrieve-pricing-information-for-different-members)

## Overview

Prices **one slot for several people at once**, returning a signed [pricing quote](../../guides/glossary.md) per person. Use it when more than one person is paying separately for the same session — a parent and a teenager on different memberships, say — because each of them attracts their own membership pricing and their own usage-subscription cover.

Send an array. Every item must carry the **same** `slotReference` and a **different** `member.id`; both rules are enforced and either breach fails the whole call with `400`. Responses come back in `pricingDetails` in the order you sent them, one for one.

The call is **all or nothing**. Items are priced one at a time and the first failure aborts the batch, returning that item's status and message for the entire request — you never get a partial result, and you cannot tell from the response body which item failed. Price items individually if you need to know.

Where this sits in the booking flow: get a slot reference from an availability endpoint such as [List all available sessions for a specific member](list-all-available-sessions-for-a-specific-member.md), price it here, then create a lease and confirm the booking passing each quote through **exactly as returned**. Each quote is signed and timestamped, and booking creation rejects a quote that has been altered or is more than an hour old.

Discounts are handled at the group level. When the booking discounts feature is enabled for the tenant, the endpoint looks up the discounts available for the activity on that date, works out the cheapest combination across everyone in the request, and reports it in `bookingDiscountDetails`. The `subTotal`, `discountTotal` and `grandTotal` fields are always returned whether or not the feature is on; the individual `pricingDetails[].totalPrice` values are **never** discounted, so take `grandTotal` as the amount to collect.

## Endpoint

`POST {base_url}/api/pricing/slots`

`{base_url}` is the environment URL issued to your integration. See [Authenticating with Gladstone APIs](../../guides/authenticating-with-gladstone-apis.md).

## Request body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `[].member` | object | Yes | The person this quote is for. One array item per person paying separately for the same slot — for example a parent and child booking the same class on separate memberships. |
| `[].member.id` | integer (int32) | Yes | The member being quoted. Send `0` to price the slot for a non-member, which changes the rules: only then may you set `member.priceLevelId`, and pricing falls back to the site's default price level if you do not. Any other value must be a member that exists, or the request is rejected with `400 MemberID {id} does not exist`. Must be unique across the array. |
| `[].member.attending` | boolean | Yes | Whether this person occupies a place themselves. Set `false` when they are only paying for guests listed in `additionalBookees`; their own place is then left out of the price entirely and `breakdown.memberPrice` is omitted from the response. If `attending` is `false` and there are no additional bookees, the request is rejected with `400 At least one bookee is required for a pricing quote`. Usage-subscription cover is only considered when this is `true`. |
| `[].member.priceLevelId` | string | No | The [price level](../../guides/glossary.md) to charge this person at. **Only valid for non-members** (`member.id` of `0`) — sending it alongside a real member id fails with `400 Price Levels cannot be specified for members`, because a member's price level is resolved from their record and their membership overrides. Omit it for a non-member and the site's default price level is used. The level must exist and must permit purchase by non-members. |
| `[].slotReference` | string | Yes | The [slot reference](../../guides/glossary.md) being priced, taken verbatim from `locations[].slots[].slotReferences.inCentre` or `.virtual` on a sessions response such as [List all available sessions](list-all-available-sessions-version-1.md). It is an opaque JSON string; do not build or edit one. **Every item in the array must carry the identical value** — this endpoint prices one slot for several people, not several slots. Mixed values fail with `400 All quotes in the request must use the same slotReference.` |
| `[].additionalBookees` | array of object | No | Guests this person is paying for on top of their own place, grouped by price level. Defaults to empty. Only accepted for party activities — sending any on a non-party activity fails with `400 Additional Bookees can only be specified for party activities`. |
| `[].additionalBookees[].priceLevelId` | string | No | The price level these guests are charged at. Each price level may appear **once** per item — repeat it and the request fails with `400 Duplicate price levels cannot be specified`. The level must exist (`404` otherwise) and must allow non-member purchase (`400 One or more add-on price levels cannot be purchased`). |
| `[].additionalBookees[].count` | integer (int32) | No | How many guests at this price level. Must be at least `1`; `0` fails with `400 One or more bookees must be specified for each price level`. The total across all price levels, plus one if `member.attending` is `true`, is checked against the activity's maximum attendees. |
| `[].isMember` | boolean | No | **Read-only — anything you send is ignored.** It is derived as `true` when `member.id` is greater than `0`. Control it through `member.id`. |
| `[].isMemberAttending` | boolean | No | **Read-only — anything you send is ignored.** Computed as `isMember` and `member.attending` together. Control it through `member.id` and `member.attending`. |

## Sample request

```bash title="cURL"
curl -X POST '{base_url}/api/pricing/slots' \
  -H 'Authorization: Bearer {accessToken}' \
  -H 'X-Use-SSO: true' \
  -H 'Content-Type: application/json' \
  -d '[
  {
    "member": {
      "id": 12345,
      "attending": true,
      "priceLevelId": "string"
    },
    "slotReference": "string",
    "additionalBookees": [
      {
        "priceLevelId": "string",
        "count": 1
      }
    ],
    "isMember": true,
    "isMemberAttending": true
  }
]'
```

## Response fields

| Field | Type | Description |
|-------|------|-------------|
| `pricingDetails` | array of object | One quote per request item, in the same order — index `n` of this array answers index `n` of your request. Each quote is signed and priced independently. |
| `pricingDetails[].memberId` | integer (int32) | The member this quote is for, echoing `member.id` from the matching request item. `0` for a non-member quote. |
| `pricingDetails[].totalPrice` | object | What this one person owes for the slot, covering their own place and their guests. It is the sum of `breakdown.priceLevels[].totalPrice`. Discounts are **not** applied here — for the discounted figure use `grandTotal`. |
| `pricingDetails[].totalPrice.amount` | number (double) | The amount, in major units of `currency`. Zero when the booking is covered by a usage subscription. |
| `pricingDetails[].totalPrice.currency` | string | The currency configured for the site the activity belongs to. |
| `pricingDetails[].breakdown` | object | How the total was arrived at, so you can show the member a line-by-line price. |
| `pricingDetails[].breakdown.memberPrice` | object | The cost of this person's **own** place, split out for display. It is a view of one of the entries already in `priceLevels`, not an extra charge — **do not add it to `totalPrice`**. Omitted entirely when `member.attending` was `false`. |
| `pricingDetails[].breakdown.memberPrice.priceLevelId` | string | The price level actually applied to this person. For a member it is resolved from their record and then improved on: if a membership carries a price level override that is cheaper for this slot, the cheaper one is used, so it may differ from the level you would predict. |
| `pricingDetails[].breakdown.memberPrice.totalPrice` | number (double) | The cost of this person's own place, being the sum of `productPrices[].amount × quantity`. Forced to zero when the booking is covered by a usage subscription. |
| `pricingDetails[].breakdown.memberPrice.productPrices` | array of object | The individual charges making up that place — a court hire and an equipment charge, for instance. One entry per priced product on the activity. |
| `pricingDetails[].breakdown.memberPrice.productPrices[].id` | string | The product being charged for. |
| `pricingDetails[].breakdown.memberPrice.productPrices[].quantity` | integer (int32) | How many units of the product are needed for one place. Multiply by `amount` to get the line cost. |
| `pricingDetails[].breakdown.memberPrice.productPrices[].timeBandId` | string | The [time band](../../guides/glossary.md) the slot falls in, which is why the same product can cost different amounts at different times of day. |
| `pricingDetails[].breakdown.memberPrice.productPrices[].amount` | number (double) | The unit price, chosen from the in-centre or virtual price according to which `slotReferences` value you priced. Forced to zero when a usage subscription covers the booking. |
| `pricingDetails[].breakdown.memberPrice.productPrices[].taxCodeId` | string | The tax code applied to this line. |
| `pricingDetails[].breakdown.memberPrice.productPrices[].taxRate` | number (double) | The rate for that tax code. `amount` is the gross price, so this is for display and reporting rather than something to add on. |
| `pricingDetails[].breakdown.currency` | string | The currency all amounts in the breakdown are expressed in. Always the same as `totalPrice.currency`. |
| `pricingDetails[].breakdown.priceLevels` | array of object | Every price level charged on this quote, one entry each, **including the member's own** — their level appears here with `count` incremented to include them. These entries are what `totalPrice` sums, so this array is the authoritative breakdown. |
| `pricingDetails[].breakdown.priceLevels[].count` | integer (int32) | How many people are charged at this level: the `count` you sent for the matching `additionalBookees` entry, plus one if this is also the member's own price level and they are attending. |
| `pricingDetails[].breakdown.priceLevels[].pricePerAdditionalBookee` | number (double) | The price for **one** person at this level, being the sum of `productPrices[].amount × quantity`. Despite the name it is also the per-person price of the member's own level. |
| `pricingDetails[].breakdown.priceLevels[].id` | string | The price level identifier. Match it against `memberPrice.priceLevelId` to find the member's own line. |
| `pricingDetails[].breakdown.priceLevels[].totalPrice` | number (double) | `pricePerAdditionalBookee × count` — the cost of everyone at this level. Forced to zero when a usage subscription covers the booking. |
| `pricingDetails[].breakdown.priceLevels[].productPrices` | array of object | The individual charges making up one place at this level. Same shape and meaning as `memberPrice.productPrices`. |
| `pricingDetails[].breakdown.priceLevels[].productPrices[].id` | string | The product being charged for. |
| `pricingDetails[].breakdown.priceLevels[].productPrices[].quantity` | integer (int32) | How many units of the product one place needs. |
| `pricingDetails[].breakdown.priceLevels[].productPrices[].timeBandId` | string | The time band the slot falls in. |
| `pricingDetails[].breakdown.priceLevels[].productPrices[].amount` | number (double) | The unit price for the in-centre or virtual variant you priced. |
| `pricingDetails[].breakdown.priceLevels[].productPrices[].taxCodeId` | string | The tax code applied to this line. |
| `pricingDetails[].breakdown.priceLevels[].productPrices[].taxRate` | number (double) | The rate for that tax code; `amount` is already gross. |
| `pricingDetails[].paidForbyUsageSub` | string | The [usage subscription](../../guides/glossary.md) covering this booking, if the member holds one with uses left for the activity. When set, every amount on the quote is zeroed and no payment is due. Null when there is none, when `member.attending` was `false`, or when the activity is a party activity — party bookings can never be paid by usage subscription. |
| `pricingDetails[].usagesRemaining` | integer (int32) | Uses left on that subscription. `0` means none was applied, and `paidForbyUsageSub` is null alongside it. |
| `pricingDetails[].timeStamp` | string | When the quote was signed, ISO-8601 with offset. It is checked when you use the quote to create a booking: a quote **older than one hour** is rejected, as is one dated more than ten minutes in the future. Price first, book promptly. |
| `pricingDetails[].signature` | string | An HMAC over the whole quote, computed with a server-side key. Booking creation recomputes it and rejects the quote with `The pricing quote is invalid.` if anything differs, so pass the quote object back **byte for byte** — reordering, rounding or re-serialising the amounts will break it. |
| `bookingDiscountDetails` | array of object | The discounts that were applied across the whole request. Present only when the booking discounts feature is enabled for the tenant **and** a discount combination beat the undiscounted price; otherwise null. The discounts available for the activity on the slot's date are evaluated and the cheapest combination for the group as a whole is picked, which is why discounts sit at the top level rather than on individual quotes. |
| `bookingDiscountDetails[].discountType` | string | The configured discount that was applied. The values are tenant configuration, so treat this as a label to display rather than a fixed set. |
| `bookingDiscountDetails[].priceLevelComboID` | integer (int32) | The identifier of the price-level combination that qualified for the discount — the configured grouping of levels, such as two adults and two juniors, that the discount was defined against. |
| `bookingDiscountDetails[].priceLevelID` | string | Always the literal `STAND`, so it carries no information — do not branch on it. |
| `bookingDiscountDetails[].discountValue` | number (double) | The change in price this discount caused, calculated as discounted cost minus base cost — so it is **negative**. Note the opposite sign to `discountTotal`. |
| `subTotal` | number (double) | The sum of every `pricingDetails[].totalPrice.amount`, before discounts. Always returned. |
| `discountTotal` | number (double) | The total taken off, as a **positive** number (the negated sum of `bookingDiscountDetails[].discountValue`). `0` when no discount applied or the feature is off. |
| `grandTotal` | number (double) | What the group actually owes: `subTotal` when nothing was discounted, otherwise the discounted cost. This is the figure to show and to collect. |

## Sample response

```json title="200 OK"
{
  "pricingDetails": [
    {
      "memberId": 12345,
      "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
              }
            ]
          }
        ]
      },
      "paidForbyUsageSub": "string",
      "usagesRemaining": 1,
      "timeStamp": "string",
      "signature": "string"
    }
  ],
  "bookingDiscountDetails": [
    {
      "discountType": "string",
      "priceLevelComboID": 12345,
      "priceLevelID": "string",
      "discountValue": 10.5
    }
  ],
  "subTotal": 10.5,
  "discountTotal": 10.5,
  "grandTotal": 10.5
}
```

## Errors

| Status | Meaning | When |
|--------|---------|------|
| `400` | Bad request | The array is empty; the items do not all share one `slotReference` (`All quotes in the request must use the same slotReference.`); two items use the same member (`Each quote in the request must use a different Member ID.`); the slot reference cannot be read; `member.id` is not `0` and does not exist (`MemberID {id} does not exist`); `member.priceLevelId` was sent for a real member (`Price Levels cannot be specified for members`); `additionalBookees` were sent for a non-party activity; a bookee entry has `count` of `0`; a price level is repeated; nobody is being charged (`At least one bookee is required for a pricing quote`); a price level does not permit purchase; the slot is not sellable at the resolved price level; or the attendee count exceeds the activity's maximum. |
| `404` | Not found | A price level named in `member.priceLevelId` or `additionalBookees[].priceLevelId` does not exist: `One or more price levels do not exist`. |
| `422` | Unprocessable entity | The activity has several pricing products on different calendars, which cannot be priced: `Multiple pricing products with different calendars for activity "{activityId}" is not permitted`. Fix the activity configuration; retrying will not help. |
| `500` | Internal server error | An unexpected server-side failure, including an unexpected failure while pricing. No body is returned. |
| `503` | Service unavailable | Pricing or booking discounts could not be reached: `At least one of the services is unavailable.` Nothing was priced; the call is safe to retry. |
