Weaverse Content API
SkillCommerce & financeUse when reading or updating live Weaverse content programmatically — pulling projects/pages/theme settings, bulk-editing page content, pushing AI-generated copy, deleting pages, or uploading media to Shopify for use in Weaverse. Triggers on requests to update a Weaverse project via API, push content to a live project, run bulk content edits, or upload images/assets to Shopify from automation.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Weaverse Content API skill
What this skill tells your AI
The instructions your AI receives, as published by weaverse/shopify-hydrogen-skills in skills/weaverse-content-api/SKILL.md and read by ahel’s review.
Read and edit live Weaverse content (projects, pages, theme settings, languages) outside the Studio editor, over an authenticated REST API. Use it for bulk edits, AI/automation content pipelines, and pushing updates into a project that already exists.
- Base URL:
https://studio.weaverse.io/api/v1/content - Machine spec:
GET https://studio.weaverse.io/api/v1/content/openapi.json(OpenAPI 3.1, no auth) — the authoritative contract - Full endpoint details:
references/endpoints.md. Rich-text/Portable Text details:references/portable-text.md. - Helper script:
scripts/weaverse_content_api.mjs(zero-dependency, Node 18+).
The one thing you must understand first
The Content API edits live content — and it can also create pages.
Two write paths exist:
PATCH .../pages/:type/*handleshallow-mergesdatainto items on the page. Existing ids are updated; an unknown id is created when its entry also supplies atype(the component type).childrenmay be supplied to relink an item's children in the same request — each entry needs anidthat already belongs to the page or is created in the same request.POST /projects/:projectId/pagescreates a page — aCUSTOMpage (bespoke merchant page, blank root) or a resource-backed template page (PRODUCT/COLLECTION/PAGE/BLOG/ARTICLE, a per-resource override that clones the project's shared default template or abasedOnsource page).
So the lifecycle is:
Create initial structure → import a project JSON into Studio (generating-weaverse-project-json)
OR create pages one by one via POST /projects/:projectId/pages
Update content afterwards → Content API (PATCH page content, incl. new typed items)
There is no "create project" endpoint — a project must already exist (from Weaverse Builder) before the API can touch it.
When to Use
- Push AI-generated or translated copy into an existing live project
- Bulk-edit content across many pages/items
- Read current page content/items to diff or round-trip
- Add a new typed item to a page (via
PATCHwith atypeon the new id) or relinkchildren - Create a
CUSTOMpage or a resource-backed template page (POST /projects/:projectId/pages) - Delete pages in bulk
- Upload an image/video to Shopify and reference its CDN URL in a Weaverse item
Do not use it to create projects — a project must already exist in Weaverse Builder.
Authentication
Every endpoint except openapi.json needs a bearer token:
Authorization: Bearer <WEAVERSE_API_KEY>
- Get the key from Weaverse Studio → Dashboard → Account/Settings → API Keys.
- A token is scoped to one shop. Requests for a project owned by another shop return
403 FORBIDDEN. - The same token also authorizes the Shopify proxy (see "Upload resources to Shopify").
- Store it in an env var (
WEAVERSE_API_KEY). Never hardcode it, never pass it as a?apiKey=query param outside local testing — query params leak into server/CDN logs.
Core update workflow
Read before you edit. To change existing content you must target real item ids, so read the page first — you cannot patch blindly. New items are the one exception: they use a fresh id plus a type, and must fit the page tree (a children reference has to point at an id already on the page or created in the same request).
- Find the project —
GET /projects, match by name, keep itsid. - Pick a locale —
GET /projects/:projectId/languages. Keep theisDefault: truecode (e.g.en-us). You need it for the next steps. - Read the page —
GET /projects/:projectId/pages/:type/*handle?locale=<code>. Always passlocale. With nolocalethe resolver only tries the empty locale and the legacy defaulten-us, so a market-first project or any project whose default locale isn'ten-usreturnsPAGE_NOT_FOUNDeven though the page exists. The defaultweaverseformat already returns every item with itsid— that id is exactly what the patch needs, so?meta=trueis not required (it only matters forportable-textreads). - Build the patch — for each item you want to change, send only the fields that change inside
data(it shallow-merges, so untouched fields stay). To create a new item, give it a freshidand supply itstype(component type). To relink children, addchildrenwith the child ids (each must already belong to the page or be created in the same request). Include the samelocaleyou read with:{ "locale": "en-us", "items": [ { "id": "itm1", "data": { "heading": "New heading" } }, { "id": "itm-new", "type": "Hero", "data": { "heading": "Fresh section" }, "children": [{ "id": "itm1" }] } ] } - Patch —
PATCH /projects/:projectId/pages/:type/*handle(usePOSTif your client/proxy can't send aPATCHbody). The page is resolved with the same locale rules as the read — a missing/wronglocalecan hitPAGE_NOT_FOUNDor patch the wrong locale's page. Max 100 items per request — chunk larger edits. - Check the response —
{ object: "page_update", updated, notFound, updatedIds, notFoundIds }. A non-emptynotFoundIdsmeans those ids couldn't be resolved on the page (wrong page, wrong locale, stale ids, or a new id sent without atype) — re-read the page with the rightlocale, don't retry the same ids.
A successful patch invalidates caches and goes live through api.weaverse.io — the same path a Studio save takes.
Page addressing
Pages are addressed by Prisma page type + handle:
INDEX, PRODUCT, ALL_PRODUCTS, COLLECTION, COLLECTION_LIST, PAGE, BLOG,
ARTICLE, CART, CUSTOMER, NOT_FOUND, PASSWORD, SEARCH, CUSTOM
- Singletons (
INDEX,ALL_PRODUCTS,COLLECTION_LIST,CART,CUSTOMER,NOT_FOUND,PASSWORD,SEARCH) — one page per project, omit the handle. - CUSTOM — addressed by its path (the splat may contain slashes, e.g.
blogs/news). - Templated (
PRODUCT,COLLECTION,PAGE,BLOG,ARTICLE) — keep a shared default template at the empty handle, so a missing handle is rejected (it won't silently edit the template). Pass the real handle.
Locale always matters. On reads/updates, always pass a real locale code (from List languages). In list-pages responses, a row's locale may be null for market-first projects (rows are keyed by market, not locale) — don't echo null back; pass a real code and let resolution map it to the market (e.g. locale=en-us resolves market us).
See references/endpoints.md for the full endpoint list, query params, and response shapes.
Upload resources to Shopify
The Content API itself has no upload endpoint. To get media into a Weaverse item, upload it to Shopify first, then reference the returned CDN URL.
Upload goes through the Weaverse Shopify proxy, which accepts the same Weaverse token:
POST https://studio.weaverse.io/api/admin-graphql
Authorization: Bearer <WEAVERSE_API_KEY>
Content-Type: application/json
The body is a normal Shopify Admin GraphQL request ({ "query": "...", "variables": {...} }). Upload is the standard two-step Shopify flow:
stagedUploadsCreate→ get aurl+parameters(a presigned target) and aresourceUrl.- Upload the file bytes to that staged
urlwith the returnedparameters(multipart POST, not through the proxy). fileCreatewithoriginalSource: <resourceUrl>→ Shopify ingests it and returns the permanent CDN file.- Read back the file's
image.url/sourcesand put that CDN URL into the Weaverse itemdatavia the update workflow above.
Reference implementation in the builder repo: app/backend/admin/file.server.ts (generateStagedUploadLinks → stagedUploadsCreate, then fileCreate). When in doubt, mirror its mutations and field selections.
Alternatively, when a connected Shopify MCP is available, its image-upload /
graphql_mutationtools do the same job without the proxy. Use whichever is connected.
Helper script
scripts/weaverse_content_api.mjs wraps auth and the common calls. It reads WEAVERSE_API_KEY from the environment.
export WEAVERSE_API_KEY=...
node scripts/weaverse_content_api.mjs projects
node scripts/weaverse_content_api.mjs languages <projectId>
node scripts/weaverse_content_api.mjs theme <projectId>
node scripts/weaverse_content_api.mjs pages <projectId> [type]
node scripts/weaverse_content_api.mjs page <projectId> <type> [handle] [locale] # reads with ?locale
node scripts/weaverse_content_api.mjs update <projectId> <type> <handle> <patch.json>
node scripts/weaverse_content_api.mjs delete <projectId> <type> <handle...>
Use it to inspect a project quickly and to apply patch files. For anything the script doesn't cover, call the REST endpoints directly or read openapi.json.
Red Flags
- Trying to create a project via the API — there is no create-project endpoint. Projects are created in Weaverse Builder; the API edits them.
- Sending a new item id without a
typein a PATCH — unknown ids are created only whentypeis supplied; otherwise they land innotFoundIds. Forchildrenentries, each id must already be on the page or be created in the same request. - Patching an existing item without reading its id first — you must target a real item id. Read the page first (with the right
locale); the defaultweaverseread already includes every itemid, so you do not need?meta=true. (Creating a new item is different: fresh id +type.) - Omitting
localeon a page read/update — with nolocalethe resolver only tries the empty locale and legacyen-us, so non-en-usor market-first projects returnPAGE_NOT_FOUNDeven when the page exists. Always pass a reallocalefrom List languages. - "My published edit is missing from the API" — silent wrong-locale page (not a 404). Content is stored per locale (
PageAssignmentkeyed byprojectId, locale, type, handle). When a merchant edits/publishes with a market locale selected (e.g.en-us), the change saves to theen-usassignment, not the base locale"". Becauselocaledefaults to"", a request like…/pages/PRODUCT/default(no?locale) can succeed (200) but return the base-locale assignment — a different, often empty/stale page — so the edit looks "missing" even though it published fine. This is distinct fromPAGE_NOT_FOUND: the request works, it just returns the wrong locale's page. Fix: always pass?locale=<market>(e.g.en-us). To find which locale a product/page uses: Studio top-bar template dropdown (shows active template + "Assigned to N products") with the market/locale selector beside it; orGET /pages(locale per row); or readdata-weaverse-template-idfrom the live storefront HTML. Verified live on a market-first project: the edit lived onlocale=en-us(active, freshly published) while baselocale=""was the stale page the no-locale call returned. - Echoing back
locale: null— list-pages rows can benullfor market-first projects. Don't sendnull; pass a real code and let resolution map it to the market. - Adding
?meta=truefor normal edits — it only affectsportable-textreads (where it restores_weaverse.id). On aweaverse-format read it changes nothing. - Ignoring
notFoundIdsin the response — it means your ids aren't on the resolved page (wrong page, wrong locale, or stale ids). Re-read with the right locale, don't retry. - Sending more than 100 items in one update (or 500 targets in one delete) — chunk the request.
- Editing a templated type with an empty handle — rejected by design. Pass the real handle.
- Hardcoding the token or using
?apiKey=— useAuthorization: Bearerfrom an env var. - Replacing whole
dataobjects — updates shallow-merge. Send only changed fields; don't resend the entiredataand risk wiping nested values you didn't read. - Putting a non-Shopify URL into a media field after "upload" — finish the
fileCreatestep and use the returned Shopify CDN URL, not the staged/temporaryresourceUrl.
Related skills
generating-weaverse-project-json— creates the import JSON that establishes the structure this API then updates. The item ids you patch here come from that JSON (or from a page read back with the rightlocale).cloning-websites-to-weaverse/figma-to-weaverse— produce the section plan that feeds the JSON generator.
Signals
- GitHub stars
- 87
- Forks
- 26
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
weaverse-content-api- Source
- github.com/weaverse/shopify-hydrogen-skills