Skip to content
Log in

Content API

On this page

The content API is the set of endpoints an agent or integration uses to read and build a store’s catalog and content: products, categories, pages, articles, content blocks, menus, media and themes. The MCP connector calls the same endpoints.

The endpoints sit under /api/v1/content. Every write goes onto a draft rather than the live store, as described in Content staging.

Find the endpoint contract

The store publishes the full endpoint contract as an OpenAPI document:

```text

https:///api/v1/openapi.json ```

The document is public and needs no credential, so you can read it before you have a key. Use it for paths, parameters, request bodies and response shapes. This article covers behavior the document does not describe.

:::note The content API and the OpenAPI document are served at the root of the domain, not under a store path. For a store at https://shop.example.com/au, use https://shop.example.com/api/v1/openapi.json. The MCP endpoint and the MCP discovery documents are different: they do use the store path. See Connect an AI agent to your store. :::

Authenticating

Send one of two credentials as a bearer token:

  • A store credential, in three colon-separated parts, org_id:store_sfid:api_token. The credential names the store, so it reaches that store whichever address it is sent to.
  • An OAuth 2.1 access token, obtained through the agent sign-in flow. The token is bound to the store it was issued for, and is refused by any other store.

For a store served at a path, an OAuth token is refused on /api/v1, because the token is bound to the store path and the API is served at the domain root. Use a store credential for that store, or connect to the store at the root of the domain.

Both credentials carry the scopes of the Salesforce user behind them, taken from the user’s Store Role. A request that needs a scope the user does not have is refused with a 403, so a user whose role grants no scopes can authenticate but cannot read or write any content.

For example, to list the first 100 products changed since the start of March:

```text

curl “https://shop.example.com/api/v1/content/products?limit=100&updated_since=2026-03-01T00:00:00Z” \ -H “Authorization: Bearer 00D5g000004XyZ1EAA:a1B5g00000AbCdEFGH:example-api-token” ```

:::warning Setting the api.disable_path_access store variable refuses authenticated API requests made on the store’s own domain, including this API and the MCP endpoint. Do not set it on a store where anyone connects an agent. See Store variables. :::

Scopes

Each resource group has a read scope and a write scope, such as content.read and content.write, so you can give a credential only the access it needs. The authoring scopes are content.write, catalog.write, themes.write and publish.

These rules decide who can create, push and discard a draft:

  • A read scope never allows publishing. A credential that can read content cannot push a draft to Salesforce.
  • Any authoring scope can push a draft the credential owns.
  • Creating a draft through the REST API needs content.write. Every other REST write names a draft the credential already owns, so a credential with only catalog.write cannot start one over REST. Through MCP, the first write opens a draft automatically, so a credential with only catalog.write can stage changes and push them.
  • Discarding a draft needs content.write through MCP, or any authoring scope through the REST API.

Paging through results

Collections are paginated with a cursor rather than an offset. Records change while a draft is being edited, and an offset would skip or repeat rows.

Each response includes a pagination object. When pagination.has_more is true, pass the value of pagination.cursor as the cursor parameter to fetch the next page. When has_more is false, you have the last page.

Set the page size with limit. The default is 50 and the maximum is 200.

Fetching only what changed

Pass updated_since to a list request to fetch only records changed after a timestamp. Results are ordered by when each record was last changed, then by id, so records that share a timestamp still page in a stable order.

:::warning updated_since must be an ISO 8601 timestamp, such as 2026-03-01T00:00:00Z. A value that cannot be parsed is ignored, and the full collection is returned instead of the changes. :::

Keeping a copy in sync

updated_since does not report deleted records. To keep a local copy in sync, including deletions, use the changes endpoint for the resource, such as GET /api/v1/content/products/changes. Pages, articles, categories, products, menus, themes, media, content blocks and the trait resources each have one. See the OpenAPI document for the exact paths.

  1. Send the first request without a since parameter.
  2. Store the sync_token from the response. The response also contains the changed records in data, and the ids of deleted records in deleted.
  3. While has_more is true, send the request again with since set to the latest sync_token.
  4. On the next sync, send since with the last stored sync_token.

A sync token expires 90 days after it is issued. A request with an expired token is refused with a 422. Start again from step 1.

Was this article helpful?

Was this article helpful?