GraphQL
Mutations
Creating, updating, publishing and deleting content.
Mutations change content. Each one needs a specific permission, and runs in the environment the request asks for: send ?environment=staging to change staging content without touching production.
Every mutation returns a result object with success. When it is false, error says why, and the request itself still succeeds with 200. Always check it:
1const { createContentEntry: result } = await graphql(CREATE, { input });2if (!result.success) throw new Error(result.error);Entries
Create an entry
Needs content.edit. Creating an entry as published also needs content.publish.
1mutation CreatePost($input: CreateContentEntryInput!) {2 createContentEntry(input: $input) {3 success4 error5 entry {6 id7 title8 slug9 status10 publishedAt11 }12 }13}{ "input": { "contentTypeId": "p8d2k4m9x0s1", "title": "Release notes for October", "slug": "release-notes-october", "status": "draft", "language": "en-US", "fields": { "excerpt": "What shipped this month.", "body": "<p>Lots.</p>" } }}| Input | Required | Description |
|---|---|---|
contentTypeId | yes | The content type's ID (not its slug). Look it up with contentType(slug:). |
title | yes | The entry's title. |
slug | yes | Unique within the content type. |
fields | yes | Object keyed by field slug. Pass {} for none. Fields you leave out get their default value, if the field has one. |
status | no | draft (default), published or archived. published sets publishedAt. |
language | no | Defaults to en-US. |
Update an entry
Needs content.edit. Changing whether an entry is published (publishing a draft, or moving a published entry back to draft or archived) also needs content.publish.
Publishing an entry in production (creating it as published, or publishing a draft) notifies the team, in the app and by email, according to each person's notification settings. It's attributed to whoever created the API key.
1mutation UpdatePost($id: ID!, $input: UpdateContentEntryInput!) {2 updateContentEntry(id: $id, input: $input) {3 success4 error5 entry { id title status publishedAt updatedAt }6 }7}{ "id": "z0q8w1e2r3t4", "input": { "title": "Release notes: October", "fields": { "excerpt": "Everything that shipped in October." } }}Every input property is optional; only the ones you send change. fields is merged: field slugs you send are overwritten, the others keep their values.
Publish and unpublish
Publishing is an update of status:
1mutation Publish($id: ID!) {2 updateContentEntry(id: $id, input: { status: "published" }) {3 success4 error5 entry { status publishedAt }6 }7}Publishing sets publishedAt the first time and keeps it on later re-publishes. Setting status: "draft" clears it.
If the entry's content type has a workflow, changing status to or from published returns an error: those entries are published by moving them through the workflow in the dashboard. Creating them as published is refused the same way.
Delete an entry
Needs content.delete. Deleting removes the entry and all its field values and can't be undone.
1mutation DeletePost($id: ID!) {2 deleteContentEntry(id: $id) {3 success4 message5 error6 }7}A missing ID returns success: false with error: "Entry not found".
Content types
All three need content_types.manage.
Create a content type
1mutation CreateType($input: CreateContentTypeInput!) {2 createContentType(input: $input) {3 success4 error5 contentType { id slug fields { slug fieldType order } }6 }7}{ "input": { "name": "Products", "slug": "products", "description": "Items in the catalogue", "fields": [ { "name": "Name", "slug": "name", "fieldType": "text", "isRequired": true }, { "name": "Price", "slug": "price", "fieldType": "number", "validation": { "min": 0 } }, { "name": "Photo", "slug": "photo", "fieldType": "media" }, { "name": "In stock", "slug": "in-stock", "fieldType": "boolean", "defaultValue": "true" } ] }}Each field takes name, slug and fieldType, and optionally description, isRequired, defaultValue (a string), fieldOptions, validation and order (defaults to its position in the list). fieldType must be one of text, textarea, html, datetime, color, media, reference, number, boolean, select, json, repeater or section; anything else returns success: false naming the field.
Update a content type
Changes the type's own properties. Every input is optional: name, slug, description, icon and isActive.
1mutation RenameType($id: ID!) {2 updateContentType(id: $id, input: { name: "Catalogue items" }) {3 success4 error5 contentType { id name slug }6 }7}Fields can't be added or changed through this mutation; edit them in the dashboard.
Delete a content type
1mutation DeleteType($id: ID!) {2 deleteContentType(id: $id) {3 success4 message5 error6 }7}A content type that still has entries can't be deleted: the result is success: false with error: "Cannot delete content type with existing entries". Delete its entries first.
From the SDK
The SDK wraps the entry mutations and throws an GraphError when success is false:
1const entry = await graph.graphql.createContentEntry({2 contentTypeId: 'p8d2k4m9x0s1',3 title: 'Release notes for October',4 slug: 'release-notes-october',5 fields: {},6});78await graph.graphql.updateContentEntry(entry.id, { status: 'published' });9await graph.graphql.deleteContentEntry(entry.id);See JavaScript SDK.