GraphQL Mutations
Mutations create, update, and delete content. Content mutations require a user JWT in addition to your site credentials — they represent editor actions, not anonymous API calls.
Authentication
headers: { 'Content-Type': 'application/json', 'x-client-id': 'YOUR_CLIENT_ID', 'x-client-secret': 'YOUR_CLIENT_SECRET', 'Authorization': 'Bearer YOUR_JWT'}Get a JWT by exchanging the signed-in user's session token with mintJwt — or let @stormycms/core's client do it automatically from the session cookie. See API Reference → Authentication for the full flow.
Page Mutations
Create a Page
mutation CreatePage( $slug: String! $metadata: metadataInput! $components: [componentInput!]! $layoutId: ID! $createdAt: String! $updatedAt: String!) { createPage( slug: $slug metadata: $metadata components: $components layoutId: $layoutId createdAt: $createdAt updatedAt: $updatedAt ) { id slug metadata { title description keywords } layoutId }}Variables:
{ "slug": "about-us", "metadata": { "title": "About Us", "description": "Learn about our team", "keywords": ["about", "team"] }, "components": [ { "name": "Heading", "props": [ { "name": "text", "value": "About Us" }, { "name": "level", "value": "h1" } ] }, { "name": "Text", "props": [{ "name": "content", "value": "We build things." }] } ], "layoutId": "LAYOUT_ID", "createdAt": "2026-07-06T00:00:00.000Z", "updatedAt": "2026-07-06T00:00:00.000Z"}Component name values must match exports in your project's CMS export map (see Components & Schema) or they won't render.
Update a Page
mutation UpdatePage( $id: ID! $slug: String! $metadata: metadataInput! $components: [componentInput!]! $layoutId: ID! $updatedAt: String!) { updatePage( id: $id slug: $slug metadata: $metadata components: $components layoutId: $layoutId updatedAt: $updatedAt ) { id slug metadata { title } updatedAt }}Updates replace the page's component tree with the provided components array.
Delete a Page
mutation DeletePage($id: ID!) { deletePage(id: $id) { id }}Layout Mutations
Create a Layout
mutation CreateLayout($name: String!, $components: [componentInput!]!, $createdAt: String!, $updatedAt: String!) { createLayout(name: $name, components: $components, createdAt: $createdAt, updatedAt: $updatedAt) { id name outletId parentLayoutId childLayoutId }}Include an Outlet component in components to mark where page content should render.
Update a Layout
mutation UpdateLayout( $id: ID! $name: String! $components: [componentInput!]! $parentLayoutId: ID $updatedAt: String!) { updateLayout(id: $id, name: $name, components: $components, parentLayoutId: $parentLayoutId, updatedAt: $updatedAt) { id name parentLayoutId }}Set parentLayoutId to nest this layout inside another (e.g. a "Blog" layout inside the site-wide layout).
Delete a Layout
mutation DeleteLayout($id: ID!) { deleteLayout(id: $id) { id }}Media Mutations
Upload the file to your own S3-compatible storage first (the boilerplate admin handles this), then record it. These mutations need your site credentials (x-client-id / x-client-secret) just like content reads; the official Next.js admin client also passes the editor JWT, but the API authorizes the request via the site API key.
mutation CreateMedia($type: String!, $url: String!, $name: String!, $metadata: Json!) { createMedia(type: $type, url: $url, name: $name, metadata: $metadata) { id type url name }}mutation UpdateMedia($id: ID!, $name: String) { updateMedia(id: $id, name: $name) { id name }}mutation DeleteMedia($id: ID!) { deleteMedia(id: $id) { id }}Auth Mutations
Used by the auth flow itself (wired automatically by @stormycms/next):
mutation ExchangeAuthCode($code: String!, $clientId: String!) { exchangeAuthCode(code: $code, clientId: $clientId) { sessionToken user { id username email } }}mutation GetJWTToken($sessionToken: String!) { mintJwt(sessionToken: $sessionToken) { jwt expiresIn }}Using the Typed Client
import { StormyCMSClient } from '@stormycms/core';
const client = new StormyCMSClient();const jwt = await client.getJWTToken(cookieHeader);
const now = new Date().toISOString();const page = await client.createPage(jwt, { slug: 'about-us', metadata: { title: 'About Us', description: '', keywords: [] }, components: [], layoutId: 'LAYOUT_ID', createdAt: now, updatedAt: now,});Next Steps
- GraphQL Queries - Read your content back
- API Reference - Full operation reference including site/team/user mutations
- Components & Schema - How component names map to your React components
Last updated: 7/9/26, 6:42 AM