# media_collection_apply
Create or modify a playlist, OTT/serial series or course collection in `C00001`, `C00002`, `C00006` or `C00007`.
## Inputs and actions
Provide channelCode, exact pAppCode, action, expectedRevision, requestId and optional dryRun (default false). Use [media_collections_get](media_collections_get.md) list creationRevision for create, the dedicated labels-view revision for label assignments, and detail revision for other actions. All resource IDs below are positive safe integers. See the common guide for revision/requestId formats.
| action.kind | Fields and behavior |
| --- | --- |
| `create` | title, optional isPublished (default false) |
| `edit` | seriesId, nonempty changes described below |
| `publication` | seriesId, isPublished |
| `delete` | seriesId, confirmation `DELETE_COLLECTION` |
| `connect` | seriesId, unique postIds (1–200 per request) |
| `disconnect` | seriesId, episodeId |
| `move` | seriesId, episodeId, beforeEpisodeId; null moves to the end |
| `curriculum` | seriesId, change; course only, described below |
| `labels` | seriesId and change:{operation:attach/detach/replace,labelId}; OTT/course only, with its labels-view revision |
Titles must be nonblank, 1–50 characters, with the stricter basic-VOD 2–30 bound. Edit changes may include title and app-specific fields: basic-VOD description 2–2048; serial description empty or 2–5000; course description up to 5000, instructor up to 30 and preparingMessage up to 20. OTT uses `ott:{director,casting,synopsis,productionYear}` with limits 30, 200, empty or 2–500, and four digits respectively. The year maps to the source's year-only date representation. An OTT object must be nonempty; unsupported app fields are rejected.
Flat connect/disconnect/move actions are unavailable for courses. OTT/serial collections allow at most 100 linked episodes; the 200 postIds envelope is a per-call bridge bound, not a 200-episode allowance. Existing connections and ordered neighbor identities are validated before editing.
## Course curriculum changes
| change.kind | Fields |
| --- | --- |
| `add-chapter` | title, 1–30 characters |
| `rename-chapter` | itemId, title, 1–30 characters |
| `add-sessions` | chapterId, unique postIds (1–200) |
| `remove` | itemId, confirmation `REMOVE_CURRICULUM_ITEM` |
| `move` | itemId, optional chapterId, beforeItemId (null for end) |
| `preview` | unique itemIds (1–200), isPreview |
Course sessions must be unconnected; the resulting course is bounded to 200 sessions. Adding a coaching session requires the installed Z00004 extension. A chapter moves at the root without chapterId; a session moves within the selected destination chapter, before a sibling or to the end. Only session items can be previews. Removing an item removes curriculum connections, not the original source posts; the last chapter cannot be removed. Chapter, session item, episode and post identities remain distinct.
## Results and recovery
Dry run returns `MEDIA_COLLECTION_PLAN`. Apply validates current state and observes the exact created/changed resource, ordered connections and course topology/previews as applicable. Creation requires the returned collection identity; a lost identity is not recovered by guessing the title. Deletion confirmation requires detail absence and a complete stable list scan bounded to 5000 collections. A bound or response failure leaves the result unconfirmed.
Task type: `media-collection`. Use the saved requestId as taskId with the matching taskType. Media `task_status` and `task_resume` are observation-only, including prepared work. Repeat identical apply to continue a prepared action or an acknowledged next unsent stage; never change requestId to bypass an uncertain sent operation. A matching readback does not prove causality, atomic isolation or restoration. See [recovery and revisions](../guides/media-workflows.md#recovery-and-revisions).
There is no general undelete, deleted-identity restoration, image/cover writer or acquisition configuration in these actions. The later collection-label assignment extension is described below.
Read the [media workflow guide](../guides/media-workflows.md) for authorization, source-versus-runtime availability and common recovery rules.
## Input example
Illustrative input only. Replace the channel, installed app, identifiers and revision with values obtained for the selected target.
```json
{
"channelCode": "selected-channel",
"pAppCode": "C00002",
"action": {
"kind": "edit",
"seriesId": 1,
"changes": {
"title": "Selected series"
}
},
"expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
"requestId": "example-request-001",
"dryRun": true
}
```
## Attach, detach or replace collection labels
Read [the collection labels view](media_collections_get.md#ott-and-course-collection-labels), copy its dedicated revision to expectedRevision, and select an exact labelCatalog ID. Supply action.seriesId and change:{operation,labelId}; both IDs are positive safe integers. A normal detail revision is insufficient.
| change.operation | Prerequisite and effect |
| --- | --- |
| attach | Target is not linked and its category is below the limit: OTT COUNTRY/AGE_RATING1 or GENRE3; course AGE_RATING1 or TOPIC3. Adds one assignment. |
| detach | The exact target is currently linked and still matches the catalog. Removes only that assignment, not the catalog label or collection. |
| replace | Target is not already linked, and exactly one other label of the same single-value category is linked. Replaces COUNTRY or AGE_RATING with the selected catalog label. |
An already-linked attach/replace, unlinked detach, unknown ID, unsupported app/category or exceeded limit is refused. Replace is not a bulk GENRE/TOPIC rewrite and cannot initialize an empty single-value category; use attach or individual detach operations. Type is derived from the verified catalog, never caller input. No catalog creation/rename/deletion, publication change or general reorder is added.
The existing exact-consumer API transport sends one POST /seller/api/v1/serieses/:seriesId/labels with {labelId}, PUT to that path with {labelIds:[labelId],type}, or DELETE /seller/api/v1/serieses/:seriesId/labels/:labelId. Context and the collection/catalog snapshot are reread immediately before the sent checkpoint and dispatch. Changes to catalog id/name/type invalidate the revision. Full preservation covers parsed non-label collection fields, publication, episodes/connected post projections, course curriculum and preview state. Unrelated parsed drift or catalog changes prevent confirmed readback. Global catalog counts/order and unknown top-level fields excluded by the existing parser are not preservation claims; sorted assignment IDs are not a requested display order.
Use the existing media-collection task and original requestId. A new dry run returns MEDIA_COLLECTION_PLAN with the verified series and no mutation or durable task. After sending, repeated apply and task_status/task_resume only observe; a lost acknowledgement never causes an automatic second request or rollback. Fresh matching readback returns MEDIA_COLLECTION_OBSERVED, currentStateMatches:true and phase:done; mismatch remains MEDIA_COLLECTION_UNCONFIRMED with pending status. This verifies observed state, not causality, atomic isolation or restoration. Existing journals without labelCatalog keep their original-action contract.
Eligible current reads, plans and observations use the existing exact series/:seriesId or classes/:seriesId destination. No new hook, transport route allowance, task namespace or method is required.
Illustrative dry runs only; use the exact collection, catalog IDs and labels-view revision. The examples assume, respectively, an available genre slot, a linked genre, and one existing country different from the selected country:
```json
{"channelCode":"selected-channel","pAppCode":"C00002","requestId":"collection-label-attach-001","expectedRevision":"0000000000000000000000000000000000000000000000000000000000000000","dryRun":true,"action":{"kind":"labels","seriesId":7,"change":{"operation":"attach","labelId":3}}}
```
```json
{"channelCode":"selected-channel","pAppCode":"C00002","requestId":"collection-label-detach-001","expectedRevision":"0000000000000000000000000000000000000000000000000000000000000000","dryRun":true,"action":{"kind":"labels","seriesId":7,"change":{"operation":"detach","labelId":3}}}
```
```json
{"channelCode":"selected-channel","pAppCode":"C00002","requestId":"collection-label-replace-001","expectedRevision":"0000000000000000000000000000000000000000000000000000000000000000","dryRun":true,"action":{"kind":"labels","seriesId":7,"change":{"operation":"replace","labelId":9}}}
```