weaverse-content-api — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited weaverse-content-api (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
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.
https://studio.weaverse.io/api/v1/contentGET https://studio.weaverse.io/api/v1/content/openapi.json (OpenAPI 3.1, no auth) — the authoritative contractreferences/endpoints.md. Rich-text/Portable Text details: references/portable-text.md.scripts/weaverse_content_api.mjs (zero-dependency, Node 18+).The Content API updates content — it does NOT create pages or projects.
PATCH .../pages/... shallow-merges data into items already on the page. Item ids that don't exist are reported as notFound and silently skipped — never created. There is no "create page" or "create project" endpoint.
So the lifecycle is:
Create initial structure → import a project JSON into Studio (use generating-weaverse-project-json)
Update content afterwards → Content API (this skill)If you need to build a brand-new storefront, page, or section, this is the wrong tool — generate import JSON with generating-weaverse-project-json and import it once. Come back here for everything after that: editing copy, swapping images, localizing, bulk edits, deletes.
Do not use it to create pages, create projects, or add new sections/blocks to a page.
Every endpoint except openapi.json needs a bearer token:
Authorization: Bearer <WEAVERSE_API_KEY>403 FORBIDDEN.WEAVERSE_API_KEY). Never hardcode it, never pass it as a ?apiKey= query param outside local testing — query params leak into server/CDN logs.Updating content is always read ids first, then patch by id. You cannot patch blindly — you must target existing item ids.
GET /projects, match by name, keep its id.GET /projects/:projectId/languages. Keep the isDefault: true code (e.g. en-us). You need it for the next steps.GET /projects/:projectId/pages/:type/*handle?locale=<code>. Always pass `locale`. With no locale the resolver only tries the empty locale and the legacy default en-us, so a market-first project or any project whose default locale isn't en-us returns PAGE_NOT_FOUND even though the page exists. The default weaverse format already returns every item with its `id` — that id is exactly what the patch needs, so `?meta=true` is not required (it only matters for portable-text reads).data (it shallow-merges, so untouched fields stay). Include the same `locale` you read with: {
"locale": "en-us",
"items": [
{ "id": "itm1", "data": { "heading": "New heading" } }
]
}PATCH /projects/:projectId/pages/:type/*handle (use POST if your client/proxy can't send a PATCH body). The page is resolved with the same locale rules as the read — a missing/wrong locale can hit PAGE_NOT_FOUND or patch the wrong locale's page. Max 100 items per request — chunk larger edits.{ object: "page_update", updated, notFound, updatedIds, notFoundIds }. A non-empty notFoundIds means those ids aren't on the resolved page (wrong page, wrong locale, or stale ids) — re-read the page with the right locale, 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.
Pages are addressed by Prisma page type + handle:
INDEX, PRODUCT, ALL_PRODUCTS, COLLECTION, COLLECTION_LIST, PAGE, BLOG,
ARTICLE, CART, CUSTOMER, NOT_FOUND, PASSWORD, SEARCH, CUSTOMINDEX, ALL_PRODUCTS, COLLECTION_LIST, CART, CUSTOMER, NOT_FOUND, PASSWORD, SEARCH) — one page per project, omit the handle.blogs/news).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.
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/jsonThe body is a normal Shopify Admin GraphQL request ({ "query": "...", "variables": {...} }). Upload is the standard two-step Shopify flow:
stagedUploadsCreate → get a url + parameters (a presigned target) and a resourceUrl.url with the returned parameters (multipart POST, not through the proxy).fileCreate with originalSource: <resourceUrl> → Shopify ingests it and returns the permanent CDN file.image.url / sources and put that CDN URL into the Weaverse item data via 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_mutation tools do the same job without the proxy. Use whichever is connected.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.
generating-weaverse-project-json + import for new structure.locale); the default weaverse read already includes every item id, so you do not need ?meta=true.locale the resolver only tries the empty locale and legacy en-us, so non-en-us or market-first projects return PAGE_NOT_FOUND even when the page exists. Always pass a real locale from List languages.PageAssignment keyed by projectId, locale, type, handle). When a merchant edits/publishes with a market locale selected (e.g. en-us), the change saves to the en-us assignment, not the base locale "". Because locale defaults 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 from PAGE_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; or GET /pages (locale per row); or read data-weaverse-template-id from the live storefront HTML. Verified live (PwC/Westside, project lzum53gvsbhehjr3in906myu): edit lived on locale=en-us (active, freshly published), base locale="" was the stale one the no-locale call returned.null for market-first projects. Don't send null; pass a real code and let resolution map it to the market.portable-text reads (where it restores _weaverse.id). On a weaverse-format read it changes nothing.Authorization: Bearer from an env var.data and risk wiping nested values you didn't read.fileCreate step and use the returned Shopify CDN URL, not the staged/temporary resourceUrl.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 right locale).cloning-websites-to-weaverse / figma-to-weaverse — produce the section plan that feeds the JSON generator.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.