> For the complete documentation index, see [llms.txt](https://docs.stellic.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.stellic.com/stellic-partner-api/catalog-sections/bulk-replace-timings-for-multiple-sections.md).

# Bulk-replace timings for multiple sections

Use this endpoint to replace the schedules of many sections in one call, for example at term open or after a room change. Each entry pairs a `section` with its complete `timings`. Use `PUT /catalog/section/timings/{id}` to replace one section's schedule.

## PUT /catalog/section/timings/bulk

>

```json
{"openapi":"3.0.0","info":{"title":"Stellic for Developers","version":"1.0"},"tags":[{"name":"Catalog Sections"}],"servers":[{"url":"https://{institution}.stellic.com/partnerapi/v1","variables":{"institution":{"default":"{institution}","description":"Your institution's Stellic subdomain: the part before `.stellic.com` in the URL you use to sign in. It becomes the host in every request, as in `https://{institution}.stellic.com/partnerapi/v1/...`. Replace the `{institution}` placeholder with your subdomain before sending."}}}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Personal Access Token issued by your Stellic integration partner. Send it as `Authorization: Bearer <token>` on every request. One token per institution; do not share across environments."}},"schemas":{"UpdateSectionTimingsBulkRequest":{"type":"object","properties":{"section":{"description":"Catalog ID of the section. When several sections share this ID, only one of them is updated. List each section once; a section listed twice gets the meetings from both entries.","type":"string"},"timings":{"description":"Full replacement schedule for the section. An empty array, or omitting the key, removes all of the section's meetings.","type":"array","items":{"$ref":"#/components/schemas/ClassTimingsWithListDays"}}},"required":["section","timings"]},"ClassTimingsWithListDays":{"type":"object","properties":{"start":{"description":"Start time as `HH:MM` (24-hour), or `TBA` or an empty string for TBA. An invalid time returns a 400 when `start_date` is neither missing nor `TBA`, on both timings endpoints.","type":"string"},"end":{"description":"End time as `HH:MM` (24-hour), or `TBA` or an empty string for TBA. An invalid time returns a 400 when `start_date` is neither missing nor `TBA`, on both timings endpoints.","type":"string"},"day":{"description":"A day can be a string or an array of names (each becomes its own row). `TBA`, an empty string, or an empty array means no weekday; an array can also contain `TBA`. Names are matched case-insensitively after surrounding whitespace is removed and stored lowercase. Unknown names return a 400.","oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"room":{"description":"Room in which class will be conducted","type":"string"},"building":{"description":"Name of the building where class will be conducted","type":"string"},"start_date":{"description":"Start date as `YYYY-MM-DD`, or `TBA`. If it's missing or `TBA`, the block's times, room, building, and end date are all treated as TBA on both timings endpoints. An unreadable start date returns a 400.","oneOf":[{"type":"string","format":"date"},{"type":"string","enum":["TBA"]}]},"end_date":{"description":"End date of the class timings if the timings are only for specific dates. An unreadable end date returns a 400 when `start_date` is neither missing nor `TBA`.","type":"string","format":"date"}},"required":["start","end","day","room","building"]},"UpdateSectionTimingsResponseTimingItem":{"type":"object","description":"One grouped meeting block. Rows that share the same start time, end time, start date, and end date are collapsed under one entry with a `days` array, even when their rooms or buildings differ.","properties":{"time":{"type":"object","description":"Start and end of the meeting block. Times are returned in 12-hour format (for example `06:00PM`), not the 24-hour format used on the request, and as `TBA` when the block has no time.","properties":{"start":{"type":"string"},"end":{"type":"string"},"campus_timezone":{"type":"string","description":"Campus timezone abbreviation derived from the campus configuration; may be `null` if no timezone is configured."}}},"days":{"type":"array","description":"Lowercase weekday names that share this slot. May contain `TBA` for rows stored with a null weekday.","items":{"type":"string"}},"building":{"type":"string","description":"Building of the grouped rows. When the rows in a group have different buildings, only one of them is shown."},"room":{"type":"string","description":"Room of the grouped rows. When the rows in a group have different rooms, only one of them is shown."},"start_date":{"type":"string","format":"date","nullable":true},"end_date":{"type":"string","format":"date","nullable":true}}}}},"paths":{"/catalog/section/timings/bulk":{"put":{"tags":["Catalog Sections"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UpdateSectionTimingsBulkRequest"}}}}},"responses":{"200":{"description":"Timings replaced on every listed section, in one transaction. Concurrent requests that include the same section run one at a time, so the last one to finish wins. One section is updated per catalog ID, even when several sections share it. After the writes succeed, the cached data and seat counts are cleared for the updated sections. Catalog search updates asynchronously after the schedules are saved, so an immediate search can still show the old schedules. With a supplied `start_date`, a meeting with a valid start time and `end` set to `\"TBA\"` or `\"\"` is accepted and saved, but its catalog search update fails; this is not merely a visibility delay. Supply a valid end time whenever you supply a valid start time. Acceptance of a TBA end does not guarantee that catalog search reflects the saved schedule. The response is an array, one entry per updated section, in the order the platform looked them up (not necessarily the order you sent); each entry's `timings` groups rows that share the same start time, end time, start date, and end date under a `days` array, with times echoed in 12-hour format.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Catalog ID of the section."},"timings":{"type":"array","items":{"$ref":"#/components/schemas/UpdateSectionTimingsResponseTimingItem"}}}}}}}},"400":{"description":"A required meeting field is missing or null, a day is unknown, or a time or date cannot be parsed. Values replaced by TBA as described in the request schema are not parsed. Validation rejects the whole batch before any section's meetings change. The validation message identifies the section with the prefix `Course section with id: {id}: `.\n\nValidation messages after that prefix include:\n- `{fields} fields are required for \"timings\" and were not found in request body`, where `{fields}` is a list of missing field names.\n- `\"{field}\" is a required field and cannot be null`\n- `Invalid day: `{value}`. Expected a weekday name or `TBA``\n- `Invalid time format: `{value}`. Expected format is `HH:MM``\n- `Invalid date format: `{value}`. Expected format is `YYYY-MM-DD``\n\nDuplicate meeting blocks across separate entries for the same section can also return 400 with `Please check your data for duplicate timings on the same course section and try again.` The whole batch is rolled back. Within each entry's `timings`, identical blocks are merged after parsing, so otherwise identical meetings written as `9:00` and `09:00` count as one meeting.","content":{"application/json":{"schema":{"type":"object","properties":{"errors":{"type":"array","items":{"type":"object","properties":{"message":{"type":"string"}}}}}}}}},"401":{"description":"Missing or invalid Bearer token."},"403":{"description":"Your IP address isn't on the allowlist for this endpoint. Contact Stellic to add it; the response includes the `client_ip` Stellic saw.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"client_ip":{"type":"string"}}}}}},"404":{"description":"A section in the payload was not found. The request is rejected before any change, so every section keeps its existing meetings.\n\nCommon causes:\n- Any section `{id}` unknown - returns `Course section with id: {id} does not exist`"},"500":{"description":"A meeting block couldn't be saved. The whole batch is rolled back, so every section keeps its existing meetings."}}}}}}
```

#### What happens after the call

* Every listed section's meetings are replaced right away, in one transaction. Pushes that include the same section run one at a time, so the last one to finish wins.
* Enrollments aren't affected; only the meeting times change.
* Catalog search updates asynchronously after the schedules are saved. An immediate search can still show the old schedules; see the TBA-end limitation below.

#### Notes

{% hint style="warning" %}
**Each section's schedule is replaced, not merged.** A meeting you leave out is removed, and an empty or missing `timings` clears that section's schedule.
{% endhint %}

* **Send `start_date` with every dated meeting.** Omitting it or sending `"TBA"` discards the supplied times, end date, room, and building.
* **Supply a valid end time whenever you supply a valid start time.** With a supplied `start_date`, a meeting with `start="09:00"` and `end="TBA"` or `end=""` is accepted and saved, but its catalog search update fails. Acceptance does not guarantee that catalog search reflects the saved schedule; waiting alone does not resolve this case.
* **List each section once.** A section listed twice gets the meetings from both entries.
* **If two sections share a catalog ID, only one is updated.** The call still returns 200. Fix the duplicate IDs in your catalog.

#### Related APIs

* [`PUT /catalog/section/timings/{id}`](/stellic-partner-api/catalog-sections/replace-a-section-s-timings.md) to replace one section's schedule, with the same required-field checks.
* [`PUT /catalog/section/bulk`](/stellic-partner-api/catalog-sections/bulk-update-section-enrollment-caps.md) to update seat counts on many sections.
* [`POST /catalog/section/{id}`](/stellic-partner-api/catalog-sections/create-a-catalog-section.md) to create a section.
