Repository navigation
[API] Course Management #39059
Copy link
Copy link
Open
Description
Functional Area: Course Management
Endpoints under this functional area. Note: this issue merges two blocks that both use the "Course Management" functional-area label in the source spec — the contentstore/v0 settings/assets/tabs/transcripts group, and the contentstore/v1 rerun/settings/team/waffle-flags/group-configurations group.
| Module | API Version | Resource / Endpoint | HTTP Method | Description |
|---|---|---|---|---|
| Advanced Settings | v0 | /contentstore/v0/advanced_settings/{course_id} |
GET | Retrieves all advanced settings for a specific course, including display names, help text, current values, and deprecation status. Supports optional query parameters filter_fields (comma-separated list of fields to filter) and fetch_all (boolean to fetch all settings or only enabled ones). Returns a dictionary where each key is a setting name and the value contains display_name, help, value, deprecated, and other metadata. Handles authentication and authorization; returns 401 if unauthenticated, 403 if access denied, or 404 if course does not exist. |
| Advanced Settings | v0 | /contentstore/v0/advanced_settings/{course_id} |
PATCH | Updates one or more advanced settings for a specific course. Accepts a JSON object where each key is a setting name and the value field is updated; all other fields in the payload are ignored. Multiple settings can be updated in a single request. Returns the updated course settings in the same format as the GET endpoint. Handles authentication and authorization; returns 401 if unauthenticated, 403 if access denied, or 404 if course does not exist. |
| File Assets | v0 | /contentstore/v0/file_assets/{course_id} |
GET | Retrieves a list of all file assets associated with a specific course. Each asset object includes file (URI), title, and locked status. Handles authentication and authorization; returns 401 if unauthenticated, 403 if access denied, or 404 if course does not exist. Supports integration for AI or external tools that need to list course assets for analysis, auditing, or automated processing. |
| File Assets | v0 | /contentstore/v0/file_assets/{course_id} |
POST | Creates a new file asset for a specific course. Accepts a JSON body with locked status; the response returns the created asset with file URI, title, and locked status. Useful for programmatically adding assets to courses. |
| File Assets | v0 | /contentstore/v0/file_assets/{course_id}/{asset_key_string} |
PUT | Updates an existing file asset completely for a given course and asset key. Accepts a JSON body (e.g., locked) and returns the full updated asset object. |
| File Assets | v0 | /contentstore/v0/file_assets/{course_id}/{asset_key_string} |
PATCH | Partially updates an existing file asset for a given course and asset key. Only the fields provided in the body (e.g., locked) are updated; other fields remain unchanged. Returns the updated asset. |
| File Assets | v0 | /contentstore/v0/file_assets/{course_id}/{asset_key_string} |
DELETE | Deletes a specific file asset from a course based on asset_key_string. Returns HTTP 204 on success. |
| Heartbeat | v0 | /contentstore/v0/heartbeat |
GET | Returns the current status of the Authoring API. Response contains authoring_api_enabled boolean. Useful for monitoring service availability. |
| Link Checking | v0 | /contentstore/v0/link_check/{course_id} |
POST | Queues a Celery task to scan a course for broken links. Returns LinkCheckStatus: "Pending". Handles authentication; 401 if unauthenticated, 403 if access denied, 404 if course not found. |
| Link Checking | v0 | /contentstore/v0/link_check_status/{course_id} |
GET | Retrieves the current status of a link check task for a course. Returns LinkCheckStatus (Uninitiated, Pending, In-Progress, Succeeded, Failed, Canceled, Retrying), LinkCheckCreatedAt, and optionally detailed output including sections, units, blocks, and previous run links. Useful for AI/external systems to monitor progress and gather detailed link integrity information. |
| Rerun Link Update | v0 | /contentstore/v0/rerun_link_update/{course_id} |
POST | Queues a Celery task to update course links to the latest course rerun. Accepts action (all to update all links or single for specific links) and optional data array with URLs, types, and block IDs. Returns status: "pending". Handles 400 for invalid payloads, 401/403/404 for auth/course errors. |
| Rerun Link Update | v0 | /contentstore/v0/rerun_link_update_status/{course_id} |
GET | Retrieves the status of a course rerun link update task. Returns status (pending, in_progress, completed, failed, uninitiated) and optionally results for completed tasks, including updated URLs and success flags. |
| Course Tabs | v0 | /contentstore/v0/tabs/{course_id} |
GET | Retrieves all tabs for a course, including hidden tabs. Each tab object includes tab_id, title, type, name, visibility flags (is_hidden, is_hideable, course_staff_only), movability, and custom settings. Useful for AI/external tools analyzing course structure. |
| Course Tabs | v0 | /contentstore/v0/tabs/{course_id}/reorder |
POST | Reorders tabs in a course. Accepts an array of objects with tab_locator identifying each tab's Usage Key. Returns HTTP 204 on success. Handles auth and course access errors (401/403/404). |
| Course Tabs | v0 | /contentstore/v0/tabs/{course_id}/settings |
POST | Changes the visibility of a course tab. Accepts is_hidden boolean in the body and identifies the tab either by tab_id or tab_location (Usage Key). Returns HTTP 204 on success. Useful for programmatically hiding or showing tabs for students or staff. |
| Video Transcripts | v0 | /contentstore/v0/video_transcripts/{course_id} |
GET | Retrieves all video transcripts for a course. Each transcript includes file URI, edx_video_id, language_code, and optional new_language_code. Useful for AI tools analyzing content accessibility or generating localized versions. |
| Video Transcripts | v0 | /contentstore/v0/video_transcripts/{course_id} |
POST | Uploads a new transcript for a course video. Accepts file (formData), edx_video_id, optional language_code, and optional new_language_code. Returns the uploaded transcript object with URI and metadata. |
| Video Transcripts | v0 | /contentstore/v0/video_transcripts/{course_id} |
DELETE | Deletes all video transcripts for a course. Returns HTTP 204 on success. |
| Video Images | v0 | /contentstore/v0/videos/images/{course_id}/{edx_video_id} |
POST | Uploads an image for a specific course video. Accepts file (formData) and returns the uploaded file URI. |
| Course Rerun | v1 | /contentstore/v1/course_rerun/{course_id} |
GET | Retrieves detailed information about a specific course rerun. Returns keys including allow_unicode_course_id, course_creator_status, number, display_name, org, and run. Requires course_id as path parameter. Handles 401, 403, 404 errors for authentication, permissions, or non-existent courses. Useful for AI tools to understand course versioning and plan content updates. |
| Course Settings | v1 | /contentstore/v1/course_settings/{course_id} |
GET | Retrieves all configurable settings for a course, including display name, credit eligibility, language options, marketing settings, LMS links, possible prerequisites, sidebar HTML, upgrade deadlines, and flags for entrance exams and extended details. Supports complex nested structures such as possible_pre_requisite_courses. Requires course_id path param. Returns 401/403/404 as per auth and access. Essential for AI systems that analyze course attributes, adapt content, or recommend courses based on eligibility and metadata. |
| Course Team | v1 | /contentstore/v1/course_team/{course_id} |
GET | Returns the list of CMS users assigned as editors or instructors for a specific course. Includes flags show_transfer_ownership_hint and allow_actions. Each user object contains id, username, email, and role. Requires course_id. Returns 401/403/404 for auth or access issues. Important for AI tools coordinating workflow automation for course management, notifications, or instructor assignments. |
| Waffle Flags | v1 | /contentstore/v1/course_waffle_flags |
GET | Retrieves the list of waffle feature flags for courses. No parameters required. Returns JSON with flag configurations to control experimental or feature-specific behavior for a course. Can be used by AI systems to adapt workflows based on active flags, e.g., enabling/disabling certain features dynamically. |
| Group Configurations | v1 | /contentstore/v1/group_configurations/{course_id} |
GET | Retrieves course group configuration data including all_group_configurations and experiment_group_configurations. Each group includes id, name, usage, version, and description. Flags include should_show_enrollment_track and should_show_experiment_groups. Supports path param course_id. Handles 401/403/404 errors. Useful for AI or external systems managing cohort assignments, experiment tracking, and monitoring user segmentation. |
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
taimoor-ahmed-1 commentedon Oct 25, 2026
Standardization PR open: #39303
PR: #39303 — feat: serve course advanced settings from authoring v1
State: open
What this does
Standardizes the advanced settings resource of the Course Management area (#39059, part of #38137). This is the first of eight resource-group PRs for the area. The others (course optimizer, custom pages and tabs, file assets, video transcripts and images, course settings, group configurations, team members) follow separately.
ADRs applied
7 of 16 decisions applied on this pass; every one left open carries its reason.
ViewSetbacked by a service module, mounted withpath(...)and the sharedcourse_keyconverter, as every route inauthoring_urls.pyisinternaluntil edx-drf-extensions catalogues them, and a malformed key gets the CMS HTML 404 at routing (both pinned by tests)PolicyCacheControlrow on the first permission check against a database that lacks it, as on v0viewandfieldsselect a representation and are validated by a serializerdeprecated: truein the schema; the DEPR entry is a comment on [DEPR]: Contentstore and enrollment API versions superseded by FC-0118 #39128 until it is folded into the issue bodyget_queryset();fieldsandviewonly narrow what the caller may already readThe evidence for each tick is the compliance matrix in the PR body; nothing is ticked without a named test or gate line.
Backward compatibility
v0/views/advanced_settings.pygains docstring text only; its syntax tree with docstrings stripped equals the base. Its route, view and URL name are untouched, and v0's own tests are not edited.viewmatches v0fetch_all=0,view=fullmatches v0's default, andfieldsmatchesfilter_fields. A valid PATCH stores the same values and returns the same body. The only declared body differences (the error envelope and the fixed per-setting message) are listed verbatim in the PR body, with a table of every status difference from v0 and the test that pins it.Gates
Gate report (PR A, step 7) — 2026-10-25 15:06 — base upstream-pr/master 87cd381
versions (23 --protect paths from plan §8.1; --allow v1/authoring_urls.py, v0/views/advanced_settings.py)
files in new version dirs: 0 other files: 3 added in old dirs: 4 allowed: 2
RESULT: PASS
hygiene
Hygiene gate — 9 file(s) scanned (added lines vs upstream-pr/master)
RESULT: PASS (0 FAIL, 0 WARN)
ruff
All checks passed!
schema: SKIPPED (no platform environment)
urls: SKIPPED (no platform environment)
old-tests: SKIPPED (no platform environment)
tests: SKIPPED (no platform environment)
supplementary (not gates): v0 docstring-only AST check True; offline harness harness-a 58/58 with mutation checks; url_snapshot on hand-built snapshot PASS
Deprecations
GET/PATCH /api/contentstore/v0/advanced_settings/{course_id}are superseded byGET/PATCH /api/authoring/v1/courses/{course_key}/advanced_settings/. They aredeprecated: truein the CMS schema, and the docstrings name the successor. The view is otherwise unchanged: no runtime header. Tracked on the FC-0118 umbrella DEPR ticket, #39128; this resource's entry is posted there with the PR. Window per #39128: removed one named release after the release this ships in.Migration notes:
/api/authoring/v1/courses/{course_key}/advanced_settings/, with the trailing slash.fetch_all=0: noview. v0 default (every field):view=full.filter_fields=a,b:fields=a,b(it selects within the chosen view).fetch_allandfilter_fieldsare ignored on v1.Follow-ups
openedx-platform-sdk; the next openedx-platform-sdk regeneration gains them.src/advanced-settings/data/api.tsandsrc/pages-and-resources/data/api.jscan move to the v1 address. Pages & Resources reads v0's default full set, so it needsview=full(orfields=).CourseMetadatato tell the two apart. That is a future-version candidate.ParseError,MethodNotAllowed,NotAcceptableandUnsupportedMediaType, and render a field-keyeddetailwithout the Python repr of the error dict. Issue to be filed.PolicyCacheControl: the first permission check on a fresh database inserts the cache row on a read; seeding it by migration would make reads write-free. Issue to be filed.COURSE_NOT_FOUND_MESSAGEand an inline course-existence check inCanViewCourse. Whichever of the two lands second should build onIsExistingCourse./api/authoring/: v1 and the feat: apply FC-0118 ADRs to course content APIs #39157 v2 views keepSessionAuthenticationAllowInactiveUser; feat: serve the course outline from authoring v2 #39229 and feat: serve course containers from authoring v2 #39274 use the default. One policy for new versions of such endpoints should be agreed.Review on the PR, please — this issue stays open until it merges.
taimoor-ahmed-1 commentedon Oct 25, 2026
Standardization PR open: #39304
PR: #39304 — feat: serve the course optimizer from authoring v1
State: open
What this does
Standardizes the course optimizer resources of the Course Management area (#39059, part of #38137). This is PR B of the eight resource-group PRs for the area. #39303 (advanced settings) is the first; custom pages and tabs, file assets, video transcripts and images, course settings, group configurations and team members follow separately.
ADRs applied
8 of 16 decisions applied on this pass; every one left open carries its reason.
ViewSets backed by a service module, mounted withpath(...)and the sharedcourse_keyconverter, as every route inauthoring_urls.pyisinternal, and per-link errors underdataare rendered as Python repr strings. A malformed key gets the CMS HTML 404 at routingPolicyCacheControlrow on the first permission check against a database that lacks it, as on v0. And the report builder thatview=fullreuses would create acourse_infoblock if a reported one had been deleted since the check ran; that path is currently unreachable in practice and also exists on v0view=fulland bounded by the links the run flaggedviewselects a representation and is validated by a serializerdeprecated: truein the schema; the DEPR entry is a comment on [DEPR]: Contentstore and enrollment API versions superseded by FC-0118 #39128 until it is folded into the issue bodyget_queryset(); record visibility is django-user-tasks'StatusFilterBackend, reused unchanged through the providerThe evidence for each tick is the compliance matrix in the PR body; nothing is ticked without a named test or gate line.
Backward compatibility
v0/views/course_optimizer.pygains docstring text only; its syntax tree with docstrings stripped equals the base. Its routes, views and URL names are untouched, and v0's own tests are not edited.actionisall, v0 hands anydatait was sent to the task, which ignores it, while v1 hands[].view=fullreport is v0's report member for member. The only declared body differences, verbatim from the test module; they are quoted verbatim in the PR body.Gates
Gate report (PR B, step 7) — 2026-10-25 21:54 — base 9b4d60e (PR A head incl. shared test helpers; stacked)
versions (23 --protect from plan §8.1; --allow v1/authoring_urls.py, v0/views/course_optimizer.py)
files in new version dirs: 0 other files: 3 added in old dirs: 4 allowed: 2
RESULT: PASS
hygiene
RESULT: PASS (0 FAIL, 0 WARN)
ruff
All checks passed!
lines >120: 0
schema: SKIPPED (no platform environment)
urls: SKIPPED (no platform environment)
old-tests: SKIPPED (no platform environment)
tests: SKIPPED (no platform environment)
Deprecations
The four v0 operations are superseded as in the Summary table. They are
deprecated: truein the CMS schema, and the docstrings name the successors. The views are otherwise unchanged: no runtime header. Tracked on the FC-0118 umbrella DEPR ticket, #39128; this PR's entry is posted there with the PR. Window per #39128: removed one named release after the release this ships in.Migration notes:
/api/authoring/v1/courses/{course_key}/link_check/and.../previous_run_link_update/, with the trailing slash:POSTto start a run,GETfor the latest one.{"status": "Pending"}.LinkCheckStatus→status,LinkCheckCreatedAt→created_at,LinkCheckError→error,LinkCheckOutput→output, and in the reportdisplayName→display_name,brokenLinks→broken_links,lockedLinks→locked_links,externalForbiddenLinks→external_forbidden_links,previousRunLinks→previous_run_links(original_link,is_updated,updated_link).view=full. Without it, the reads return the run's state only.Uninitiatedon both resources (the link update read spelled ituninitiated).erroranderror_messagecarry fixed messages, not the recorded failure text.action: all,datais not read and may be omitted. Withaction: single, every link needs a non-blankurl,typeandid.Follow-ups
openedx-platform-sdk; the next openedx-platform-sdk regeneration gains them.src/optimizer-page/data/api.tscan move to the v1 addresses. It needs:view=fullfor the report and the results; the new member names (the page camel-cases responses withcamelCaseObject, so it readsstatus,createdAtandoutputwhere it readlinkCheckStatus,linkCheckCreatedAtandlinkCheckOutput); 202 on start; 404 for the feature off.ParseError,MethodNotAllowed,NotAcceptableandUnsupportedMediaType, and normalize nested validation errors (per-item objects instead of Python repr strings;detailwithout the repr of the error dict). Issue to be filed; the pinned tests flip when it lands.PolicyCacheControl: the first permission check on a fresh database inserts the cache row on a read; seeding it by migration would make reads write-free. Issue to be filed.library_content,split_test) break the four-level report on v0 and v1. A future version could report such components at the unit level. Recorded as a future-version candidate; the provider is not changed here.COURSE_NOT_FOUND_MESSAGEand an inline course-existence check inCanViewCourse. Whichever of feat: serve the course outline from authoring v2 #39229 and feat: serve course advanced settings from authoring v1 #39303 lands second should build onIsExistingCourse, which this PR reuses./api/authoring/: v1 here, feat: serve course advanced settings from authoring v1 #39303 and the feat: apply FC-0118 ADRs to course content APIs #39157 v2 views keepSessionAuthenticationAllowInactiveUser; feat: serve the course outline from authoring v2 #39229 and feat: serve course containers from authoring v2 #39274 use the default. One policy for new versions of such endpoints should be agreed.link_check/andprevious_run_link_update/are singular, one-per-course children ofcourses/{course_key}/. They follow ADR 0038's owncourses/{course_key}/details/singleton precedent: each course has one current check and one current update.rerun_link_updatewas renamed because "rerun" reads as a verb and the resource is the update of links to the previous run.Review on the PR, please — this issue stays open until it merges.
taimoor-ahmed-1 commentedon Oct 25, 2026
Standardization PR open: #39305
PR: #39305 — feat: serve course custom pages and tabs from authoring v1
State: open
What this does
Standardizes the custom pages and tabs resources of the Course Management area (#39059, part of #38137). This is PR C of the eight resource-group PRs for the area; #39303 (advanced settings) is PR A.
ADRs applied
7 of 16 decisions applied on this pass; every one left open carries its reason.
ViewSets over a service module, mounted withpath(...)like every route inauthoring_urls.py, because a router's regex routes cannot use the shared path convertersinternaland a malformed course key on the nested routes gets the CMS HTML 404 (library and routing gaps, pinned by tests);detailstill carries the library'sErrorDetail(...)text for validation errors whileerrorsis cleanPolicyCacheControlrow on the first permission check against a database that lacks it, as on v0settingsmap each) and a single tabdeprecated: truein the schema; the DEPR entry is a comment on [DEPR]: Contentstore and enrollment API versions superseded by FC-0118 #39128 until it is folded into the issue bodyget_queryset(); the layering is shown in the matrixThe evidence for each tick is the compliance matrix in the PR body; nothing is ticked without a named test or gate line.
Backward compatibility
v0/views/tabs.pygains docstring text only; its syntax tree with docstrings stripped equals the base. Its routes, views and URL names are untouched, and v0's own tests are not edited.Gates
Gate report (PR C, step 7) — 2026-10-25 21:54 — base 9b4d60e (PR A head incl. shared test helpers; stacked)
versions (23 --protect from plan §8.1; --allow v1/authoring_urls.py, v0/views/tabs.py)
files in new version dirs: 0 other files: 4 added in old dirs: 4 allowed: 2
RESULT: PASS
hygiene
WARN H3 cms/djangoapps/contentstore/rest_api/pagination.py:8 local subclass of a shared primitive: keep only if it adds behaviour and says why
RESULT: PASS (0 FAIL, 1 WARN)
ruff
All checks passed!
lines >120: 0
schema: SKIPPED (no platform environment)
urls: SKIPPED (no platform environment)
old-tests: SKIPPED (no platform environment)
tests: SKIPPED (no platform environment)
Deprecations
The three v0 operations are superseded by the three v1 operations in the Summary. They are
deprecated: truein the CMS schema, and the docstrings name each successor. The views are otherwise unchanged: no runtime header. Tracked on the FC-0118 umbrella DEPR ticket, #39128; this resource's entry is posted there with the PR. Window per #39128: removed one named release after the release this ships in.Migration notes:
GET /api/authoring/v1/custom_pages/?course_key={course_key}. It is paginated: readresults, and follownextor passpage/page_size(at most 100) to get every page. Items are unchanged, includingid.PUT /api/authoring/v1/courses/{course_key}/custom_page_order/with{"custom_pages": [<usage key>, …]}, theids from the list (v0'stab_locatorvalues). Pages left out follow the listed ones in their current order. Answers 204.PATCH /api/authoring/v1/courses/{course_key}/tabs/{tab_id}/with{"is_hidden": true|false}. Answers 200 with the tab. A body withoutis_hiddenchanges nothing.course_key,custom_pages,is_hidden).Follow-ups
openedx-platform-sdk; the next openedx-platform-sdk regeneration gains them.src/custom-pages/data/api.ts) can move to the v1 list (requestpage_size=100or follownext) and thePUTorder. No MFE caller uses the v0 settings endpoint today.DefaultPagination.get_paginated_response_schemawith all seven keys, after whichDocumentedDefaultPaginationcan be deleted. Issue to be filed.ParseError,MethodNotAllowed,NotAcceptableandUnsupportedMediaType.detailwithout theErrorDetail(...)text.CourseTextbookPagination(frozen inv2/views/textbooks.py) duplicates the shared paginator's schema; point the next version of that resource atrest_api/pagination.py.PolicyCacheControl: the first permission check on a fresh database inserts the cache row on a read; seeding it by migration would make reads write-free. Issue to be filed./api/authoring/: v1 here, feat: serve course advanced settings from authoring v1 #39303 and the feat: apply FC-0118 ADRs to course content APIs #39157 v2 views keepSessionAuthenticationAllowInactiveUser; feat: serve the course outline from authoring v2 #39229 and feat: serve course containers from authoring v2 #39274 use the default. One policy for new versions of such endpoints should be agreed.views/permissions.py,v1/authoring_urls.py,cms/lib/spectacular.pyand its test. The conflicts are additive, resolved by rebasing in merge order. When both land, the mergedopenedx_authz.constants.permissionsimport must be wrapped to stay within 120 characters.Review on the PR, please — this issue stays open until it merges.
taimoor-ahmed-1 commentedon Oct 25, 2026
Standardization PR open: #39306
PR: #39306 — feat: serve course team members from authoring v2
State: open
What this does
Standardizes the course team resource of the Course Management area (#39059, part of #38137). This is PR H of the eight resource-group PRs for the area; #39303 (advanced settings) is PR A and #39305 (custom pages and tabs) is PR C.
ADRs applied
7 of 16 decisions applied on this pass; every one left open carries its reason.
ViewSetover a service module, mounted withpath(...)like every route inv2/authoring_urls.py, because a router's regex routes cannot use the shared path convertersinternal, and a malformed course key or an address no route matches gets the CMS HTML 404 (library and routing gaps, pinned by tests); the publishedErrorResponsecomponent's text comes from edx-drf-extensionsPolicyCacheControlrow on the first permission check against a database that lacks it, as on v1memember of the same resource, because the caller's flags cannot ride the page envelopemeof five scalarsdeprecated: truein the schema; the DEPR entry for [DEPR]: Contentstore and enrollment API versions superseded by FC-0118 #39128 is to be posted with this PR, and no removal release is named yetget_queryset(); the team is read through Studio's course role helpers; the layering is shown in the matrixThe evidence for each tick is the compliance matrix in the PR body; nothing is ticked without a named test or gate line.
Backward compatibility
v1/views/course_team.pygains docstring text only; its syntax tree with docstrings stripped equals the base. Its route, view and URL name are untouched, and v1's own tests are not edited.mesays what v1's body says about the caller, for every member;Gates
Gate report (PR H, step 7) — 2026-10-25 — base 8721326 (PR C head; stacked on C and A)
versions (23 --protect; --allow v1/views/course_team.py (docstring marker), v2/authoring_urls.py)
RESULT: PASS (2 allowed, 4 added in existing v2 dirs)
hygiene: 9 files, RESULT: PASS (0 FAIL, 0 WARN)
ruff: All checks passed! py_compile clean lines >120: 0
pylint (4.1.3, repo pylintrc): only C1803
== [](tolerated; PR A has the same and passed CI)supplementary: offline harness-h 63/63; mutants 22/22 killed
schema / urls / old-tests / tests: SKIPPED (no platform environment)
Deprecations
The v1 operation is superseded by the two v2 operations in the Summary. It is
deprecated: truein the CMS schema, and the docstrings name both successors. The view is otherwise unchanged: no runtime header. Tracked on the FC-0118 umbrella DEPR ticket, #39128; this resource's entry is posted there with the PR. Window per #39128: removed one named release after the release this ships in.Migration notes:
GET /api/authoring/v2/courses/{course_key}/team_members/. It is paginated: readresults, and follownextor passpage/page_size(at most 100) to get every member. Items are unchanged (id,username,email,role). Instructors come first, then staff, each by username.GET /api/authoring/v2/courses/{course_key}/team_members/me/.allow_actionsiscan_manage_team;show_transfer_ownership_hintisis_only_instructor.roleis the caller's team role, ornullwhen they hold none.Follow-ups
openedx-platform-sdk; the next openedx-platform-sdk regeneration gains them.src/course-team/data/api.ts) can move to the v2 list (requestpage_size=100or follownext) and read its flags fromme. Its writes still go to Studio'scourse_team/{course}/{email}handler, which has no REST successor in this area.team_members/{username}/member (the ADR's example address), if added, must be reconciled withme: one component for a person, or the difference documented.MethodNotAllowedandNotAcceptable.ErrorResponsecomponent's description isErrorResponseSerializer's docstring, which names ADR 0029 and carries a usage example; it should describe the error body instead. Issue to be filed.DefaultPagination.get_paginated_response_schemawith all seven keys (from feat: serve course custom pages and tabs from authoring v1 #39305), after which the shared paginator can be deleted./api/that match no route, instead of the CMS HTML page. Issue to be filed.PolicyCacheControl: the first permission check on a fresh database inserts the cache row on a read; seeding it by migration would make reads write-free. Issue to be filed./api/authoring/v2/: v2 here and the feat: apply FC-0118 ADRs to course content APIs #39157 v2 views keepSessionAuthenticationAllowInactiveUser; feat: serve the course outline from authoring v2 #39229 and feat: serve course containers from authoring v2 #39274 use the default on the same mount. One policy for new versions of such endpoints should be agreed.views/permissions.py,v2/authoring_urls.py,cms/lib/spectacular.pyand its test. The conflicts are additive, resolved by rebasing in merge order.Review on the PR, please — this issue stays open until it merges.
taimoor-ahmed-1 commentedon Oct 25, 2026
Standardization PR open: #39307
PR: #39307 — feat: serve course rerun defaults and settings from authoring v2
State: open (stacked on #39303 and #39229)
What this does
Standardizes the course rerun defaults and course settings resources of the Course Management area (#39059, part of #38137). This is PR F of the eight resource-group PRs for the area; #39303 (advanced settings) is PR A and #39305 (custom pages and tabs) is PR C.
GET /api/authoring/v2/courses/{course_key}/rerun_defaults/replacesGET /api/contentstore/v1/course_rerun/{course_id}(global staff only, as v1)GET /api/authoring/v2/courses/{course_key}/settings/(view=minimalby default, orview=full) replacesGET /api/contentstore/v1/course_settings/{course_id}; it gainscontent_experiments_enabled, which replacesshould_show_experiment_groupsof the group configurations listing (PR G)ADRs applied
7 of 16 decisions applied on this pass; every one left open carries its reason.
ViewSets over service modules, mounted withpath(...)like every route inv2/authoring_urls.py, because a router's regex routes cannot use the shared path convertersinternal, and a malformed course key or an address no route matches gets the CMS HTML 404 (library and routing gaps, pinned by tests); the publishedErrorResponsecomponent's text comes from edx-drf-extensionsPolicyCacheControlrow on the first lookup against a database that lacks it, exactly as the v1 read does therecontent_experiments_enabledis one member moved from the group configurations listing, not a merged endpointdeprecated: truein the schema; the DEPR entry for [DEPR]: Contentstore and enrollment API versions superseded by FC-0118 #39128 is to be posted with this PR, and no removal release is named yetget_queryset(); the prerequisite list comes from Studio's course listing for the caller, as on v1; the layering is shown in the matrixThe evidence for each tick is the compliance matrix in the PR body; nothing is ticked without a named test or gate line.
Backward compatibility
v1/views/course_rerun.pyandv1/views/settings.pygain docstring text only; for each, the syntax tree with docstrings stripped equals the base. Their routes, views and URL names are untouched, and v1's own tests are not edited.view=fullis v1's body pluscontent_experiments_enabled, andview=minimalis that without the three heavy members, across the prerequisite, credit, publisher and verified-mode fixtures;search,ordering,active_onlyand the others) are sent;start_date_on_or_afteris ignored (200; v1 400);view=defaultis a 400 (v1 200);Gates
Gate report (PR F, step 7) — 2026-10-25 — base a1b1c07 (merge of PR A 9b4d60e into #39229 53ea5f4)
versions (23 --protect; --allow v1/views/course_rerun.py, v1/views/settings.py (docstring markers), v2/authoring_urls.py)
RESULT: PASS (8 new files in v2, 2 other files, 3 allowed)
hygiene: 13 files, RESULT: PASS (0 FAIL, 0 WARN)
ruff: All checks passed! lines >120: 0
pylint (4.1.3, repo pylintrc): only C1803
== [](tolerated; PR A has the same and passed CI)supplementary: offline harness-f 44/44; mutants 15/15 killed (v1 modelled by stand-ins)
schema / urls / old-tests / tests: SKIPPED (no platform environment)
Deprecations
Both v1 operations are superseded by the v2 operations above. They are
deprecated: truein the CMS schema, and the docstrings name the successors. The views are otherwise unchanged: no runtime header. Tracked on the FC-0118 umbrella DEPR ticket, #39128; one entry covering both is posted there with the PR. Window per #39128: removed one named release after the release this ships in.Migration notes:
GET /api/authoring/v2/courses/{course_key}/rerun_defaults/. Same six fields and values; still global staff only.GET /api/authoring/v2/courses/{course_key}/settings/?view=fullfor v1's body pluscontent_experiments_enabled. Withoutview=full(or withview=minimal),language_options,possible_pre_requisite_coursesandcredit_requirementsare left out.viewtakes onlyminimalorfull; any other value is a 400.search,ordering,active_onlyor the course listing's other parameters, and a malformedstart_date_on_or_afteris ignored.content_experiments_enabledhere instead ofshould_show_experiment_groups, andmfe_proctored_exam_settings_urlhere; the group configurations successor (PR G) does not return them.Known gaps and risks
view=minimalsaves payload, not work: the frozen shared helper builds the lists whatever the view (future-version item).settings/is named after a screen; reviewers may ask for a narrower noun..v2-suffixed while feat: serve the course outline from authoring v2 #39229's on the same mount are not; the suffix is needed because v1's settings component is published asCourseSettings.rerun_defaults("rerun" is a noun here; false positive).Follow-ups
openedx-platform-sdk; the next openedx-platform-sdk regeneration gains them.src/generic/data/api.ts) can move torerun_defaults/unchanged. The settings callers (src/data/api.ts,src/content-tags-drawer/data/api.ts) can move tosettings/, requestingview=fullwhere they use the language list, prerequisites or credit requirements. The group configurations page needs this PR before it moves to PR G's listing, because it readscontent_experiments_enabledandmfe_proctored_exam_settings_urlfrom here.view=minimal, and a bounded or referenced prerequisite list./api/authoring/v2/: agree one component-name convention (suffixed or not) for the mount, and settle thesettingsnoun in review.MethodNotAllowedandNotAcceptable.ErrorResponsecomponent's description isErrorResponseSerializer's docstring, which names ADR 0029 and carries a usage example; it should describe the error body instead. Issue to be filed./api/that match no route, instead of the CMS HTML page. Issue to be filed.PolicyCacheControl: the first permission check on a fresh database inserts the cache row on a read; seeding it by migration would make reads write-free. Issue to be filed./api/authoring/v2/: v2 here and the feat: apply FC-0118 ADRs to course content APIs #39157 v2 views keepSessionAuthenticationAllowInactiveUser; feat: serve the course outline from authoring v2 #39229 and feat: serve course containers from authoring v2 #39274 use the default on the same mount. One policy for new versions of such endpoints should be agreed.masterand the merge commit drops out.v2/authoring_urls.py,cms/lib/spectacular.pyand its test. The conflicts are additive, resolved by rebasing in merge order.Review on the PR, please — this issue stays open until it merges.
taimoor-ahmed-1 commentedon Oct 25, 2026
Standardization PR open: #39308
PR: #39308 — feat: serve group configurations from authoring v2
State: open
What this does
Standardizes the group configurations resource of the Course Management area (#39059, part of #38137). This is PR G of the eight resource-group PRs for the area; #39303 (advanced settings) is PR A, #39305 (custom pages and tabs) is PR C, and #39307 (course settings and rerun defaults) is PR F.
Stacked on #39305, which stacks on #39303. Merge after #39307 before any client moves to this endpoint: v1's
should_show_experiment_groupsandmfe_proctored_exam_settings_urlare served by #39307'sGET /api/authoring/v2/courses/{course_key}/settings/(ascontent_experiments_enabledandmfe_proctored_exam_settings_url), not by this resource.ADRs applied
8 of 16 decisions applied on this pass; every one left open carries its reason.
ViewSetover a service module, mounted withpath(...)like every route inv2/authoring_urls.py, because a router's regex routes cannot use the shared path convertersinternal, and a malformed course key or an address no route matches gets the CMS HTML 404 (library and routing gaps, pinned by tests); the publishedErrorResponsecomponent's text comes from edx-drf-extensionsPolicyCacheControlrow on the first permission check against a database that lacks it, as on v1deprecated: truein the schema; the DEPR entry for [DEPR]: Contentstore and enrollment API versions superseded by FC-0118 #39128 is to be posted with this PR, and no removal release is named yetget_queryset(); configurations are user partitions stored with the course in the modulestore; the layering is shown in the matrixThe evidence for each tick is the compliance matrix in the PR body; nothing is ticked without a named test or gate line.
Backward compatibility
v1/views/group_configurations.pygains docstring text only; its syntax tree with docstrings stripped equals the base. Its route, view and URL name are untouched, and v1's own tests are not edited.view=full, the items are v1's two arrays in order, identical apart fromcan_manage;usage;can_manageis v1'scan_manage;Gates
Gate report (PR G, step 7) — 2026-10-25 — base 8721326 (PR C head; stacked on C and A)
versions (23 --protect; --allow v1/views/group_configurations.py (docstring marker), v2/authoring_urls.py)
RESULT: PASS (2 allowed, 4 added in existing v2 dirs)
hygiene: 9 files, RESULT: PASS (0 FAIL, 0 WARN)
ruff: All checks passed! lines >120: 0
pylint (4.1.3, repo pylintrc): only C1803
== [](tolerated; PR A has the same and passed CI)supplementary: serializer-level parity mutation check (6/6 mutants caught); static check of the differences-table guard
schema / urls / old-tests / tests: SKIPPED (no platform environment)
Deprecations
The v1 operation is superseded by the v2 operation in the Summary. It is
deprecated: truein the CMS schema, and the docstrings name the successor. The view is otherwise unchanged: no runtime header. Tracked on the FC-0118 umbrella DEPR ticket, #39128; this resource's entry is posted there with the PR. Window per #39128: removed one named release after the release this ships in.Migration notes:
GET /api/authoring/v2/courses/{course_key}/group_configurations/. It is paginated: readresults, and follownextor passpage/page_size(at most 100) to get every configuration. The items are v1'sall_group_configurationsfollowed by v1'sexperiment_group_configurations, in the same order and shape.view=fullto getusageon configurations and groups, as v1 returns it.can_manage: read it from any item; it is the same on all of them and does not override an item'sread_only.should_show_enrollment_track: true exactly when an item hasschemeenrollment_track.should_show_experiment_groupsandmfe_proctored_exam_settings_url: readcontent_experiments_enabledandmfe_proctored_exam_settings_urlfromGET /api/authoring/v2/courses/{course_key}/settings/(feat: serve course rerun defaults and settings from authoring v2 #39307). Do not migrate before feat: serve course rerun defaults and settings from authoring v2 #39307 is merged.viewaccepts onlyminimalandfull; an out-of-range or non-numericpageis a 404.Follow-ups
openedx-platform-sdk; the next openedx-platform-sdk regeneration gains it.src/group-configurations/data/api.ts) can move to the v2 list (requestpage_size=100or follownext, andview=fullfor usage), readcan_managefrom the items, and read the experiments flag and the proctored exam settings link fromsettings/. Its writes still go to Studio'sgroup_configurations/{course}/…handlers, which have no REST successor in this area; agroup_configurations/{id}/member is where they would migrate.view=minimalsaves the per-configuration content scans as well as the payload (future version).MethodNotAllowedandNotAcceptable.ErrorResponsecomponent's description isErrorResponseSerializer's docstring, which names ADR 0029 and carries a usage example; it should describe the error body instead. Issue to be filed.DefaultPagination.get_paginated_response_schemawith all seven keys (from feat: serve course custom pages and tabs from authoring v1 #39305), after which the shared paginator can be deleted./api/that match no route, instead of the CMS HTML page. Issue to be filed.PolicyCacheControl: the first permission check on a fresh database inserts the cache row on a read; seeding it by migration would make reads write-free. Issue to be filed./api/authoring/v2/: v2 here and the feat: apply FC-0118 ADRs to course content APIs #39157 v2 views keepSessionAuthenticationAllowInactiveUser; feat: serve the course outline from authoring v2 #39229 and feat: serve course containers from authoring v2 #39274 use the default on the same mount. One policy for new versions of such endpoints should be agreed.views/permissions.py,v2/authoring_urls.py,cms/lib/spectacular.pyand its test. The conflicts are additive, resolved by rebasing in merge order. Inviews/permissions.py, H's class and this PR's are both appended at the end of the file; the reflowedopenedx_authzimport line is also the line feat: serve the course outline from authoring v2 #39229 (which feat: serve course rerun defaults and settings from authoring v2 #39307 stacks on) edits to addCOURSES_VIEW_COURSE.Review on the PR, please — this issue stays open until it merges.