{"title":"Content staging for API changes","slug":"content-staging","url":"https://support.storeconnect.com/articles/content-staging","url_markdown":"https://support.storeconnect.com/articles/content-staging.md","subtitle":null,"summary":"How a write through the content API reaches a live store: create a change, stage records onto it, preview it, and push it to Salesforce for a person to approve. Covers who can stage onto, discard or push a draft and what happens after it is pushed.","type":"Developer_Documentation","video_url":"","keywords":"content staging, content changes, draft, staged records, publish, push, approval, salesforce, preview url, discard, change records, agent platform, content api, mcp","last_modified":"2026-11-04T00:53:28+0000","body_markdown":"Nothing written through the content API reaches the live store directly. Writes collect on a draft, called a content change, and the change goes live only when a person approves it in Salesforce. An agent cannot publish without a person's approval, whatever scopes it has.\n\nUse this process to take a change from draft to the live store, and to check who can act on a draft at each step.\n\n## The lifecycle\n\n1.  **Create a change.** Over the REST API, send `POST /api/v1/content/changes` with a `summary`. Reviewers see the summary in the Salesforce approval queue, so describe what the change does, for example `\"Add the spring range and a new home page hero\"`. Through MCP, the first write opens a draft automatically if you have not created one.\n2.  **Stage records onto it.** Write to paths of the form `/api/v1/content/changes/{change_id}/\u003cresource\u003e`, such as `/api/v1/content/changes/{change_id}/products`. Paths that are not nested under a change return live data.\n3.  **Check what you staged.** Send `GET /api/v1/content/changes/{change_id}/records` to list every record staged on the change. Add `object_api_name` to filter by object, for example `object_api_name=s_c__page__c`.\n\n    A record you staged as new is not live, so a plain read of it returns `404`. To read one back over the REST API, add `content_change_id={change_id}` to a single-record read, such as `GET /api/v1/content/pages/{id}?content_change_id={change_id}`. This works only for your own draft. List reads, and reads of records that are already live, return live values. Through MCP, reads show the store with your draft applied.\n4.  **Preview it.** `GET /api/v1/content/changes/{change_id}/preview_url` returns a URL that shows the staged store in a browser.\n5.  **Push it.** Send `POST /api/v1/content/changes/{change_id}/publish`. The change goes to Salesforce for review, and the REST response returns only the new status and a message.\n\n    When you push through MCP, the response also lists problems found in the staged content, such as products without a price or media without alternative text. Most problems are warnings for the reviewer. One stops the push until it is fixed: a content block that names a theme snippet that does not exist.\n\n6.  **A person approves it in Salesforce.** The staged records reach the live store only at this step. Salesforce does not let a person approve a change they created.\n\n## A taken identifier is refused when it is staged\n\nAn identifier on a media record or a content block must be unique, because Salesforce rejects a duplicate when the change publishes. The media and content block endpoints check for a duplicate when you stage the record, so a clash is refused straight away instead of failing the whole change at publish. The response is a `422` naming the record that already holds the value, for example:\n\n```text\n\nIdentifier \"hero-banner\" is already used by Spring hero (a0B5g00000XyZ12EAB). These must be unique, so choose a different one or update that content block instead.\n```\n\nThe check ignores case, as Salesforce does, so `Hero-Banner` clashes with `hero-banner`. The check covers live records and records staged on the same change, including a staged rename. Keeping a record's own identifier when you update the record is not a clash.\n\n## Who can read and write a draft\n\nAnyone in the store can read any draft through `GET /api/v1/content/changes/{change_id}` and the records list, so a team can see each other's work in progress.\n\nOnly the draft's owner can stage records onto it, discard it or push it. A teammate can read your draft but cannot add to it, discard it or push it.\n\nFor which scopes allow creating, pushing and discarding a draft, see the [scope rules](content-api#scopes).\n\n## Undo or discard\n\nTo undo one staged record and keep the rest of the change, send `DELETE /api/v1/content/changes/{change_id}/records/{record_id}`, where `{record_id}` is the id returned when you staged the record. This needs `content.write`, and works only on your own draft.\n\nTo discard the whole change and everything staged onto it, send `DELETE /api/v1/content/changes/{change_id}`.\n\nYou can only discard a change while it is still a draft. Once the change is pushed, the decision is made in Salesforce, and the API cannot discard it.\n\n## Preview URLs expire\n\nStoreConnect generates a new preview URL for each request, and each URL works for a limited time. Do not store a preview URL to reuse or share later, because you cannot tell when it will stop working. Request a new one each time you need it.\n\n## After the push\n\nWhether and when a pushed change is published is decided in Salesforce. The storefront shows the change only after it is published.\n\nTo check progress, send `GET /api/v1/content/changes/{change_id}` and read `status` in the response:\n\n- `draft` — editable, and not in review.\n- `review` — pushed and waiting for a person in Salesforce.\n- `approved` — approved and waiting to be published.\n- `published` — the staged records are live on the store.\n\nIf a change stays at `review` or `approved` longer than expected, ask the person approving changes in Salesforce to check it."}