Update one or more booking sheet exceptions

PUT /api/booking-sheets/exception/{scheduleId}

Success: 200 OK

Overview

Rewrites the exceptions produced by an existing recurring schedule. An exception blocks a resource product for a period of time so that no booking can be taken against it — a maintenance window, a staff meeting, a closure. A schedule is the recurrence definition that generates those exceptions, identified by the GUID returned in scheduleId when you created it with Create one or more booking sheet exceptions.

Send the schedule's definition in full. The request body is the same shape as the create call, and it replaces the stored definition outright: the schedule's future exception records are deleted and regenerated from the times and recurrence fields you send. Records that have already finished are left alone, so past exceptions survive an update. Anything you omit — days, notes, exclusion dates — is not carried over from the previous definition.

The endpoint expands your times into individual occurrences, one per date per entry in productIds, and checks them for clashes before writing anything. Nothing is written unless every check passes; there is no partial update.

Use this endpoint to:

Read errors and exceptionsUpdated on every 200 OK. A clash sends back 400 (your own occurrences overlap each other) or 409 (they overlap existing bookings or other schedules' exceptions), in both cases with the full response body so you can see what collided. But if the recurrence produces no dates at all — for example days names a weekday that never falls inside the date range — the call succeeds with 200 OK, exceptionsUpdated: 0, an empty scheduleId and errors explaining why, and nothing has changed. Treating any 200 as success will leave you believing a closure is in place when it is not.

Endpoint

PUT {base_url}/api/booking-sheets/exception/{scheduleId}

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

Path parameters

NameTypeRequiredDescription
scheduleIdstringYesThe schedule whose exceptions you are replacing, as a GUID in 8-4-4-4-12 form. Use the scheduleId returned by Create one or more booking sheet exceptions. An empty value is rejected with Schedule ID must be specified, a value that does not parse as a GUID with Schedule ID is not a valid GUID, and a well-formed GUID with no exception records against it with 404 and No exceptions found for Schedule ID '{scheduleId}'.

Request body

FieldTypeRequiredDescription
siteIdstringYesThe site (leisure centre or venue) the exceptions belong to. Missing or empty is rejected with Site ID must be specified; a value that matches no site with Invalid Site ID '{siteId}'.
productIdsarray of stringYesThe resource products to block — the bookable items on the booking sheet, such as a court, lane or studio. Every occurrence the recurrence generates is written once for each product listed, so all of them are blocked over the same pattern. At least one is required (One or more Resource Products must be specified, or No Product IDs found in request for an empty array), the list must contain no duplicates (Duplicate Product IDs found in request), and every id must be an existing resource product (One or more Product IDs are invalid '{ids}').
daysarray of stringNoRestricts the generated occurrences to particular weekdays. Use full English day names, case-insensitive — monday through sunday; abbreviations and anything else are rejected with Invalid day in request. Valid days are: {list}, and repeats with Duplicate Days found in request. Omit it, or send an empty array, to allow every day. What is permitted depends on frequency: at least one day is required for Weekly (At least one day must be specified for Weekly frequency), exactly one for Monthly with recurranceType ByDay, and none at all for Monthly with ByDate (Days cannot be specified when RecurranceType is 'ByDate').
timesarray of objectYesThe time bands to block, each with the window over which to repeat. The date parts of an entry bound the recurrence; the time-of-day parts set the start and end of each occurrence generated within it. At least one entry is required — One or more Exception Times must be specified for a missing or empty array, At least one Exception Time must be specified for a null one. This list replaces the schedule's existing times outright.
times[].timeBandIdstringYesThe time band to block: a named period of the day configured against the booking sheet. Missing or empty is rejected with Timeband ID must be specified. The band must exist and be configured as entry-restricted, otherwise the request fails with One or more TimeBand IDs do not exist or are not marked as entry restricted '{ids}' — a band that exists but is not entry-restricted produces the same message as one that does not exist at all.
times[].dateTimeFromstring (date-time)NoThe start of the window, and the start time of day of each generated occurrence. Marked optional, but omitting it defaults to 0001-01-01T00:00:00, which is rejected with Invalid DateTimeFrom or DateTimeTo for time band {timeBandId}. It must be in the future — DateTimeFrom ({date}) cannot be in the past for time band {timeBandId}. Seconds are rounded to the nearest second before validation.
times[].dateTimeTostring (date-time)NoThe end of the window, and the end time of day of each generated occurrence. Marked optional, but rejected with Invalid DateTimeFrom or DateTimeTo for time band {timeBandId} when omitted. It must be later than dateTimeFrom (DateTimeTo ({to}) must be after DateTimeFrom ({from}) for time band {timeBandId}) and its time of day must also be later than that of dateTimeFrom (DateTimeTo time ({hh:mm}) must be after DateTimeFrom time ({hh:mm}) for time band {timeBandId}), so a single occurrence cannot cross midnight — split an overnight closure into one entry per day. Each stored occurrence ends one second before this time of day, so consecutive blocks do not register as overlapping.
notesstringNoFree-text note copied onto every exception this request generates, for whoever looks at the booking sheet later. An empty string is saved as null.
frequencystringNoHow the window in each times entry is expanded into occurrences: Daily, Weekly or Monthly, matched case-insensitively. Defaults to Daily when omitted; any other value is rejected with Invalid Frequency '{value}', must be one of 'Daily', 'Weekly', 'Monthly'. It also decides which of days, recursEvery, recurranceType and recursOn are allowed — each of those is rejected outright under the wrong frequency, rather than ignored.
recursEveryinteger (int32)NoThe gap between occurrences: weeks for Weekly, months for Monthly. Defaults to 1 for those two frequencies, and must be greater than zero (RecursEvery must be greater than zero). For Daily it must be omitted or 1 — any other value is rejected with RecursEvery cannot be specified for Daily frequency.
recurranceTypestringNoFor Monthly only, how the monthly date is chosen — note the spelling of the field. ByDate repeats on the same day of the month as the first times entry's dateTimeFrom, clamped to the last day in shorter months; ByDay repeats on the nth named weekday, combining recursOn with the single entry in days. Matched case-insensitively; an unrecognised value gives Invalid RecurranceType '{value}', must be one of 'ByDate', 'ByDay'. Required for Monthly (RecurranceType must be specified for Monthly frequency) and forbidden otherwise (RecurranceType cannot be specified for Daily frequency, ... for Weekly frequency).
recursOninteger (int32)NoWhich occurrence of the weekday within the month, for Monthly with recurranceType ByDay — 3 together with days: ["friday"] means the third Friday. Must be between 1 and 5 (RecursOn must be between 1 and 5) and is required in that combination (RecursOn must be specified when RecurranceType is 'ByDay'). It is rejected everywhere else: RecursOn cannot be specified when RecurranceType is 'ByDate', RecursOn cannot be specified for Daily frequency, RecursOn cannot be specified for Weekly frequency.
exclusionDatesarray of stringNoDates to skip. Any generated occurrence falling on one of these dates is dropped; only the date part is compared, so the time you send is irrelevant. Note that Delete booking sheet exceptions within a date range appends the deleted dates to the stored schedule's exclusion list — because this call overwrites the stored definition, omitting exclusionDates here silently reinstates dates that were previously deleted.
additionalDatesarray of stringNoAccepted and stored with the schedule definition, but no occurrences are generated from it — only times combined with the recurrence fields produce exception records. To block a one-off extra date, add another entry to times.

Sample request

cURL
curl -X PUT '{base_url}/api/booking-sheets/exception/{scheduleId}' \
  -H 'Authorization: Bearer {accessToken}' \
  -H 'X-Use-SSO: true' \
  -H 'Content-Type: application/json' \
  -d '{
  "siteId": "string",
  "productIds": [
    "string"
  ],
  "days": [
    "string"
  ],
  "times": [
    {
      "timeBandId": "string",
      "dateTimeFrom": "2026-08-14T19:00:00Z",
      "dateTimeTo": "2026-08-14T19:00:00Z"
    }
  ],
  "notes": "string",
  "frequency": "string",
  "recursEvery": 1,
  "recurranceType": "string",
  "recursOn": 1,
  "exclusionDates": [
    "2026-08-14T19:00:00Z"
  ],
  "additionalDates": [
    "2026-08-14T19:00:00Z"
  ]
}'

Response fields

FieldTypeDescription
exceptionsUpdatedinteger (int32)How many exception records were written — one per generated date per entry in productIds, so a fortnight of weekday closures over three products is 30. 0 means nothing was written and the schedule is unchanged; check errors when you see it.
scheduleIdstring (uuid)The schedule the new exception records belong to. It is the same GUID you sent in the path when records were written, and 00000000-0000-0000-0000-000000000000 when none were.
errorsarray of stringWhy the update did not proceed. Empty on a successful update. The possible entries are No valid dates found for the specified days, One or more exceptions overlap with each other, One or more exceptions overlap with existing exceptions for the same product(s) and One or more exceptions overlap with existing bookings for the same product(s). If this array is non-empty, nothing was written — including on a 200 OK, which is what you get when the only entry is No valid dates found for the specified days.
overlappingExceptionsarray of objectOccurrences generated from your own request that collide with each other — for example two times entries covering the same time band on the same day. Accompanies a 400, and is empty otherwise. Fix the request; no existing data is involved.
overlappingExceptions[].dateTimeFromstring (date-time)Start of the generated occurrence that was found to overlap another one in the same request.
overlappingExceptions[].dateTimeTostring (date-time)End of that occurrence — the time band's end time of day, less one second.
overlappingExceptions[].timeBandIdstringThe time band the overlapping occurrence was generated for.
overlappingExceptions[].productIdstringAlways null here. Self-overlap is detected on the generated dates before they are multiplied out across productIds, so no single product can be named; the collision applies to every product in the request.
existingExceptionsarray of objectExceptions already stored against the same resource products that overlap the occurrences you asked for. Accompanies a 409, and is empty otherwise. Exceptions belonging to this schedule are excluded, so a schedule never clashes with the records it is replacing — anything listed here belongs to a different schedule and must be dealt with before the update can succeed.
existingExceptions[].dateTimeFromstring (date-time)Start of the exception already in place.
existingExceptions[].dateTimeTostring (date-time)End of the exception already in place.
existingExceptions[].timeBandIdstringThe time band the existing exception was recorded against.
existingExceptions[].productIdstringThe resource product the existing exception blocks — one of the ids you sent in productIds.
existingBookingsarray of objectBookings already taken against the same resource products that overlap the occurrences you asked for. Accompanies a 409, and is empty otherwise. The update cannot proceed over a live booking: cancel or move the bookings listed here, then retry.
existingBookings[].bookingIdinteger (int32)The id of the clashing booking, for looking it up or cancelling it.
existingBookings[].dateTimeFromstring (date-time)Start of the clashing booking.
existingBookings[].dateTimeTostring (date-time)End of the clashing booking.
existingBookings[].timeBandIdstringAlways null for bookings. A booking is recorded against a date and time rather than a time band, so this field is not populated in this array.
existingBookings[].productIdstringThe resource product the clashing booking occupies — one of the ids you sent in productIds.

Sample response

200 OK
{
  "exceptionsUpdated": 1,
  "scheduleId": "b7f3c1e2-9a44-4f2b-8c17-0d5e9a1b6c33",
  "errors": [
    "string"
  ],
  "overlappingExceptions": [
    {
      "dateTimeFrom": "2026-08-14T19:00:00Z",
      "dateTimeTo": "2026-08-14T19:00:00Z",
      "timeBandId": "string",
      "productId": "string"
    }
  ],
  "existingExceptions": [
    {
      "dateTimeFrom": "2026-08-14T19:00:00Z",
      "dateTimeTo": "2026-08-14T19:00:00Z",
      "timeBandId": "string",
      "productId": "string"
    }
  ],
  "existingBookings": [
    {
      "bookingId": 12345,
      "dateTimeFrom": "2026-08-14T19:00:00Z",
      "dateTimeTo": "2026-08-14T19:00:00Z",
      "timeBandId": "string",
      "productId": "string"
    }
  ]
}

Errors

StatusMeaningWhen
400Bad requestThe path parameter is unusable: Schedule ID must be specified or Schedule ID is not a valid GUID
400Bad requestA required value is missing: Site ID must be specified, One or more Resource Products must be specified, No Product IDs found in request, One or more Exception Times must be specified, At least one Exception Time must be specified or Timeband ID must be specified
400Bad requestA value does not resolve: Invalid Site ID '{siteId}', One or more Product IDs are invalid '{ids}', One or more TimeBand IDs do not exist or are not marked as entry restricted '{ids}', Duplicate Product IDs found in request, Invalid day in request. Valid days are: {list} or Duplicate Days found in request
400Bad requestThe dates are not usable: Invalid DateTimeFrom or DateTimeTo for time band {timeBandId}, DateTimeTo ({to}) must be after DateTimeFrom ({from}) for time band {timeBandId}, DateTimeTo time ({hh:mm}) must be after DateTimeFrom time ({hh:mm}) for time band {timeBandId} or DateTimeFrom ({from}) cannot be in the past for time band {timeBandId}
400Bad requestThe recurrence fields contradict frequency: Invalid Frequency '{value}', must be one of 'Daily', 'Weekly', 'Monthly', RecursEvery cannot be specified for Daily frequency, RecursEvery must be greater than zero, RecursOn cannot be specified for Daily frequency, RecurranceType cannot be specified for Daily frequency, At least one day must be specified for Weekly frequency, RecursOn cannot be specified for Weekly frequency, RecurranceType cannot be specified for Weekly frequency, RecurranceType must be specified for Monthly frequency, Invalid RecurranceType '{value}', must be one of 'ByDate', 'ByDay', RecursOn must be specified when RecurranceType is 'ByDay', RecursOn must be between 1 and 5, Exactly 1 day must be specified when RecurranceType is 'ByDay', Invalid day name '{day}', RecursOn cannot be specified when RecurranceType is 'ByDate' or Days cannot be specified when RecurranceType is 'ByDate'
400Bad requestThe occurrences generated from your request overlap each other. The body is the full response object, not a message string, with overlappingExceptions listing the collisions and errors containing One or more exceptions overlap with each other. Nothing was written.
400Bad requestThe write itself failed: A database error occurred.
404Not foundNo exception records exist for the schedule in the path: No exceptions found for Schedule ID '{scheduleId}'. This is also what you get for a schedule whose exceptions have all been deleted, so it does not distinguish "never existed" from "emptied".
409ConflictThe occurrences you asked for overlap bookings already taken, or exceptions belonging to another schedule, on the same resource products. The body is the full response object, with existingBookings and existingExceptions naming what collided. Nothing was written; resolve the clashes and retry.
500Internal server errorAn unexpected server-side failure. Every validation failure returns 400, 404 or 409, so a 500 here is not something to work around in the request.

This page as markdown · All pages · llms.txt