curl --request PATCH \
--url https://studio.weaverse.io/api/v1/content/projects/{projectId}/pages/{type}/{handle} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"items": [
{
"id": "019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a82",
"data": {
"title": "Summer Sale"
}
},
{
"id": "019a3c2f-1a2b-7c3d-9e4f-5a6b7c8d9e01",
"type": "rich-text",
"data": {
"content": "<p>Free shipping on orders over $50.</p>"
}
},
{
"id": "019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a81",
"data": {},
"children": [
{
"id": "019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a82"
},
{
"id": "019a3c2f-1a2b-7c3d-9e4f-5a6b7c8d9e01"
},
{
"id": "019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a83"
}
]
}
]
}
'const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
items: [
{id: '019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a82', data: {title: 'Summer Sale'}},
{
id: '019a3c2f-1a2b-7c3d-9e4f-5a6b7c8d9e01',
type: 'rich-text',
data: {content: '<p>Free shipping on orders over $50.</p>'}
},
{
id: '019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a81',
data: {},
children: [
{id: '019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a82'},
{id: '019a3c2f-1a2b-7c3d-9e4f-5a6b7c8d9e01'},
{id: '019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a83'}
]
}
]
})
};
fetch('https://studio.weaverse.io/api/v1/content/projects/{projectId}/pages/{type}/{handle}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://studio.weaverse.io/api/v1/content/projects/{projectId}/pages/{type}/{handle}"
payload = { "items": [
{
"id": "019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a82",
"data": { "title": "Summer Sale" }
},
{
"id": "019a3c2f-1a2b-7c3d-9e4f-5a6b7c8d9e01",
"type": "rich-text",
"data": { "content": "<p>Free shipping on orders over $50.</p>" }
},
{
"id": "019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a81",
"data": {},
"children": [{ "id": "019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a82" }, { "id": "019a3c2f-1a2b-7c3d-9e4f-5a6b7c8d9e01" }, { "id": "019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a83" }]
}
] }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text){
"object": "page_update",
"pageId": "clz2r7t5w0002mnop9012qrst",
"locale": "en-us",
"requested": 3,
"updated": 2,
"created": 1,
"notFound": 0,
"failed": 0,
"updatedIds": [
"019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a82",
"019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a81"
],
"createdIds": [
"019a3c2f-1a2b-7c3d-9e4f-5a6b7c8d9e01"
],
"notFoundIds": [],
"failedIds": []
}{
"object": "error",
"code": "UNAUTHORIZED",
"message": "<string>",
"status": 123,
"details": "<unknown>"
}{
"object": "error",
"code": "UNAUTHORIZED",
"message": "<string>",
"status": 123,
"details": "<unknown>"
}{
"object": "error",
"code": "UNAUTHORIZED",
"message": "<string>",
"status": 123,
"details": "<unknown>"
}{
"object": "error",
"code": "UNAUTHORIZED",
"message": "<string>",
"status": 123,
"details": "<unknown>"
}{
"object": "error",
"code": "STALE_PAGE",
"message": "Page \"PRODUCT/blue-shirt\" changed since your last read (expectedUpdatedAt mismatch). Re-read the page and reapply your patch on top of the newer content.",
"status": 409
}{
"object": "error",
"code": "UNAUTHORIZED",
"message": "<string>",
"status": 123,
"details": "<unknown>"
}Update page content
Edits component items on the page resolved for type / handle / locale, using the same fallback as the GET endpoint. Pass the exact handle from the pages list; it may include /. Singleton types accept an empty handle; CUSTOM and per-resource types require one.
datais shallow-merged into the item’s existing data: send only changed top-level keys. Nested objects are replaced wholesale.- Up to 100 items per request; item ids must be unique within the request.
- An unknown item id is created only when
typeis supplied; otherwise it is returned innotFoundIds. childrenreplaces an item’s top-level child references. Each reference must point to an existing page item or a typed new item created in the same request.- A page inherited from a parent project returns
409; edit it on the source project. exact: truewrites only the assignment whose stored type, handle and locale match exactly, returning404instead of falling back. Delegated writes are always exact.
Concurrency: pass the updatedAt from your GET as expectedUpdatedAt. If the page changed since, the whole request is rejected with 409 STALE_PAGE before any item is written; re-read and reapply on top of the newer content instead of retrying the same patch. Delegated (MCP OAuth) tokens must send it (428 PRECONDITION_REQUIRED otherwise); it is optional for API keys.
On success the edit goes live through api.weaverse.io like a Studio save. Clients or proxies that cannot send PATCH bodies may send POST.
Required scope: content:write
curl --request PATCH \
--url https://studio.weaverse.io/api/v1/content/projects/{projectId}/pages/{type}/{handle} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"items": [
{
"id": "019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a82",
"data": {
"title": "Summer Sale"
}
},
{
"id": "019a3c2f-1a2b-7c3d-9e4f-5a6b7c8d9e01",
"type": "rich-text",
"data": {
"content": "<p>Free shipping on orders over $50.</p>"
}
},
{
"id": "019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a81",
"data": {},
"children": [
{
"id": "019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a82"
},
{
"id": "019a3c2f-1a2b-7c3d-9e4f-5a6b7c8d9e01"
},
{
"id": "019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a83"
}
]
}
]
}
'const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
items: [
{id: '019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a82', data: {title: 'Summer Sale'}},
{
id: '019a3c2f-1a2b-7c3d-9e4f-5a6b7c8d9e01',
type: 'rich-text',
data: {content: '<p>Free shipping on orders over $50.</p>'}
},
{
id: '019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a81',
data: {},
children: [
{id: '019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a82'},
{id: '019a3c2f-1a2b-7c3d-9e4f-5a6b7c8d9e01'},
{id: '019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a83'}
]
}
]
})
};
fetch('https://studio.weaverse.io/api/v1/content/projects/{projectId}/pages/{type}/{handle}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://studio.weaverse.io/api/v1/content/projects/{projectId}/pages/{type}/{handle}"
payload = { "items": [
{
"id": "019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a82",
"data": { "title": "Summer Sale" }
},
{
"id": "019a3c2f-1a2b-7c3d-9e4f-5a6b7c8d9e01",
"type": "rich-text",
"data": { "content": "<p>Free shipping on orders over $50.</p>" }
},
{
"id": "019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a81",
"data": {},
"children": [{ "id": "019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a82" }, { "id": "019a3c2f-1a2b-7c3d-9e4f-5a6b7c8d9e01" }, { "id": "019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a83" }]
}
] }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text){
"object": "page_update",
"pageId": "clz2r7t5w0002mnop9012qrst",
"locale": "en-us",
"requested": 3,
"updated": 2,
"created": 1,
"notFound": 0,
"failed": 0,
"updatedIds": [
"019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a82",
"019a3c2e-5b7d-7f10-8a2b-3c4d5e6f7a81"
],
"createdIds": [
"019a3c2f-1a2b-7c3d-9e4f-5a6b7c8d9e01"
],
"notFoundIds": [],
"failedIds": []
}{
"object": "error",
"code": "UNAUTHORIZED",
"message": "<string>",
"status": 123,
"details": "<unknown>"
}{
"object": "error",
"code": "UNAUTHORIZED",
"message": "<string>",
"status": 123,
"details": "<unknown>"
}{
"object": "error",
"code": "UNAUTHORIZED",
"message": "<string>",
"status": 123,
"details": "<unknown>"
}{
"object": "error",
"code": "UNAUTHORIZED",
"message": "<string>",
"status": 123,
"details": "<unknown>"
}{
"object": "error",
"code": "STALE_PAGE",
"message": "Page \"PRODUCT/blue-shirt\" changed since your last read (expectedUpdatedAt mismatch). Re-read the page and reapply your patch on top of the newer content.",
"status": 409
}{
"object": "error",
"code": "UNAUTHORIZED",
"message": "<string>",
"status": 123,
"details": "<unknown>"
}Authorizations
Bearer authentication: a shop API key (WEAVERSE_API_KEY), a scoped API key, a delegated MCP OAuth token, or (translation-unit routes only) a project-bound translation agent token. Scopes: content:read — Read projects, pages, theme settings, versions, drafts and translation units. content:write — Save drafts and make non-destructive edits (page items, metadata, SEO, assignments, languages, glossary). Never publishes or deletes. content:publish — Publish drafts, schedule/reschedule/cancel publications, restore versions, publish global sections. content:delete — Delete pages, global sections, languages, static keys, override reverts; required with project:manage for project deletion. project:manage — Create/copy/delete projects, settings, variants, hierarchy, backups, restore-as-new, previews, hostname policy. translations:read — Read translation units only (no general content access). translations:write — Apply translation units only; never mutates page content. A legacy unscoped API key keeps read/write/translation baseline access and every pre-scope endpoint, but never gains publish/delete/project-manage implicitly.
Path Parameters
Page type (e.g. PRODUCT, INDEX, CUSTOM)
Page handle. Final greedy path component; may include /. Leave empty to target a singleton type's default page (e.g. the home page for INDEX, or the /collections index COLLECTION_LIST). A handle is required for CUSTOM pages and for per-resource templated types (PRODUCT, COLLECTION, PAGE, BLOG, ARTICLE).
Body
100Show child attributes
Show child attributes
Optional. Same resolution as GET; defaults to the stored default locale.
Optimistic-concurrency token: the updatedAt value from the GET read this patch was based on. Required for delegated (MCP OAuth) tokens — omitted → 428 PRECONDITION_REQUIRED; optional for API keys. When the page changed since (e.g. a concurrent studio save), the whole request is rejected with 409 STALE_PAGE before any item is written; once an earlier item was written or may have been (its outcome unconfirmed), a move reports that item and the remaining ones as failed (not confirmed applied) with 200 instead. null matches a page whose revision was never set. Re-read and reapply on top of the newer content instead of retrying the same patch.
Refuse locale/handle fallback; 404 instead
Was this page helpful?