Create one or more booking sheet exceptions

POST /api/booking-sheets/exception

Overview

Blocks out time on a booking sheet, so no new booking can be taken against the named resource products during the periods you describe — maintenance, a closure, a school hire, anything that takes the resource off sale.

One request describes a recurring pattern, not a single block. You give a window (times[].dateTimeFrom to times[].dateTimeTo), a frequency of Daily, Weekly or Monthly, and the recurrence fields that go with that frequency; the endpoint expands the pattern into individual exceptions — one per generated date, per entry in times, per entry in productIds — and creates them all together under a single schedule id, returned as scheduleId. Keep that id: it is how you later amend or remove the whole set, with Delete booking sheet exceptions within a date range.

Nothing is created unless everything can be created. Before creating anything, the endpoint checks the generated dates against each other, against exceptions that already exist and against bookings that have already been taken:

Both bodies are the same shape as the success body, so you can read errors in every case.

One case to guard against: if the pattern generates no dates at all — every occurrence fell on an excluded date, or on a weekday not listed in days — the call still returns 201 Created, but with exceptionsCreated of 0 and No valid dates found for the specified days in errors. Check exceptionsCreated rather than trusting the status code.

Exceptions cannot be created in the past, and each generated block ends one second before the time of day you give in dateTimeTo, so consecutive blocks butt up against each other rather than overlapping.

Endpoint

POST {base_url}/api/booking-sheets/exception

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

Request body

FieldTypeRequiredDescription
siteIdstringYesThe site whose booking sheet is being blocked out; every product in productIds is interpreted within it. Required, and must be an existing site — an unknown value fails with Invalid Site ID '{siteId}', as a 400 rather than a 404. Omitting it fails with Site ID must be specified.
productIdsarray of stringYesThe resource products to take off sale — a court, a lane, a studio. The whole generated pattern is applied to each one, so a fortnight's closure across three courts is one request, not three. 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 not repeat itself (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 pattern to particular weekdays. Use full English day names — monday through sunday, matched case-insensitively; anything else fails with Invalid day in request. Valid days are: …, and repeats with Duplicate Days found in request. What it means depends on frequency: with Daily, leaving it out means every day and listing days keeps only those; with Weekly at least one day is required (At least one day must be specified for Weekly frequency); with Monthly and recurranceType of ByDay exactly one is required (Exactly 1 day must be specified when RecurranceType is 'ByDay'); with Monthly and ByDate it must be left out entirely (Days cannot be specified when RecurranceType is 'ByDate').
timesarray of objectYesThe time bands to block and the window each pattern runs over. At least one entry is required (One or more Exception Times must be specified, or At least one Exception Time must be specified for an empty array). Each entry is expanded independently, so listing three time bands over a four-week daily pattern across two products creates 3 × 28 × 2 exceptions in one go.
times[].timeBandIdstringYesThe time band on the booking sheet to block — take it from timeBandId in Get timebands. The band must exist and be marked as entry restricted; if any band in the request fails either test the whole request is rejected with One or more TimeBand IDs do not exist or are not marked as entry restricted '{ids}'. Omitting it fails with Timeband ID must be specified.
times[].dateTimeFromstring (date-time)YesDoes double duty: the date the pattern starts, and the time of day every generated block begins at. Required in practice — omitting it fails with Invalid DateTimeFrom or DateTimeTo for time band {timeBandId}. It must be in the future (DateTimeFrom ({value}) cannot be in the past for time band {timeBandId}), earlier than dateTimeTo, and its time of day must be earlier than dateTimeTo's, which is what stops a block spanning midnight. For a restriction that crosses midnight, send one entry for each side of it. With frequency of Monthly and recurranceType of ByDate, the day number of the first entry in times is the day of the month the pattern repeats on.
times[].dateTimeTostring (date-time)YesThe date the pattern stops and the time of day every generated block ends at. Required in practice, on the same grounds as dateTimeFrom, and subject to the same two comparisons. Note that each block ends one second before this time of day — give 18:00 and the exception runs to 17:59:59 — so a block ending at 18:00 and the next starting at 18:00 do not count as overlapping.
notesstringNoFree text stored against every exception this request creates, for whoever looks at the booking sheet later — the reason for the closure, a job number, a contact. It has no effect on availability, and is not used in clash detection.
frequencystringYesHow the pattern repeats: Daily, Weekly or Monthly, matched case-insensitively. Required in practice even though it is not marked as required — omitting it fails with Invalid Frequency '', must be one of 'Daily', 'Weekly', 'Monthly'. It decides which of the other recurrence fields are allowed: Daily accepts only days; Weekly requires days and accepts recursEvery; Monthly requires recurranceType and accepts recursEvery, with recursOn and days governed by that type.
recursEveryinteger (int32)NoHow many periods to skip between occurrences: with Weekly, 2 means every other week; with Monthly, every other month. Defaults to 1 when omitted for those two frequencies, and must be greater than zero (RecursEvery must be greater than zero). Meaningless for Daily — send it as anything other than 1 and the request fails with RecursEvery cannot be specified for Daily frequency.
recurranceTypestringNoMonthly only, and required there (RecurranceType must be specified for Monthly frequency). ByDate repeats on the same day number each month, taken from dateTimeFrom of the first entry in times and clamped to the length of shorter months, so the 31st becomes the 30th in April. ByDay repeats on the nth named weekday of the month — the second Tuesday, say — set through recursOn and days. Matched case-insensitively; any other value fails with Invalid RecurranceType '{value}', must be one of 'ByDate', 'ByDay'. Sending it with Daily or Weekly fails with RecurranceType cannot be specified for {frequency} frequency. Note the spelling of the field name — one r, two ns.
recursOninteger (int32)NoWhich occurrence of the weekday in the month to use, when recurranceType is ByDay: 1 for the first, through 5 for the fifth. Required in that case (RecursOn must be specified when RecurranceType is 'ByDay') and limited to that range (RecursOn must be between 1 and 5). If a month has no fifth occurrence the pattern moves forward to the next month that does, rather than falling back to the last one. Must be left out everywhere else — with Daily, with Weekly, and with recurranceType of ByDate — each of which has its own rejection message.
exclusionDatesarray of stringNoDates to drop from the generated pattern — bank holidays, a week the works were cancelled. Only the date part is compared, so the time you send is ignored, and a date that the pattern would not have produced anyway is simply not used. If exclusions remove every occurrence, the request returns 201 having created nothing; see the Overview.
additionalDatesarray of stringNoAccepted but not used: dates listed here are silently discarded and no extra exceptions are created for them. To block a date the pattern does not reach, send a second request.

Sample request

cURL
curl -X POST '{base_url}/api/booking-sheets/exception' \
  -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"
  ]
}'

Errors

StatusMeaningWhen
400Bad requestA field is missing or malformed: Site ID must be specified, One or more Resource Products must be specified, One or more Exception Times must be specified, Timeband ID must be specified, Invalid DateTimeFrom or DateTimeTo for time band {timeBandId}. The body is a plain string.
400Bad requestA date range is unusable: DateTimeTo ({to}) must be after DateTimeFrom ({from}) for time band {timeBandId}, DateTimeTo time ({to}) must be after DateTimeFrom time ({from}) for time band {timeBandId}, or DateTimeFrom ({from}) cannot be in the past for time band {timeBandId}.
400Bad requestThe recurrence fields do not match the frequency: Invalid Frequency …, RecursEvery cannot be specified for Daily frequency, At least one day must be specified for Weekly frequency, RecurranceType must be specified for Monthly frequency, RecursOn must be between 1 and 5, and the other combination rules listed against each field above.
400Bad requestA list is malformed: No Product IDs found in request, Duplicate Product IDs found in request, Invalid day in request. Valid days are: …, Duplicate Days found in request.
400Bad requestA referenced record does not exist: Invalid Site ID '{siteId}', One or more Product IDs are invalid '{ids}', or One or more TimeBand IDs do not exist or are not marked as entry restricted '{ids}'. These are 400, not 404.
400Bad requestThe exceptions the pattern generated overlap each other. The body is the full response object, with the offending pairs in overlappingExceptions and One or more exceptions overlap with each other in errors. Nothing was created.
400Bad requestSaving the exceptions failed — A database error occurred. Please ensure that the ID supplied is unique.
404Not foundThis endpoint does not return 404: every missing site, product and time band is reported as 400.
409ConflictThe generated exceptions clash with exceptions or bookings that already exist. The body is the full response object: existingExceptions and existingBookings name the date range, time band and product for each clash, and existingBookings also carries the bookingId you would need to cancel. Nothing was created — resolve the clashes and send the request again.
500Internal server errorAn unexpected server-side failure, with an empty body.

This page as markdown · All pages · llms.txt