Graphdocs

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 success
4 error
5 entry {
6 id
7 title
8 slug
9 status
10 publishedAt
11 }
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>"
}
}
}
InputRequiredDescription
contentTypeIdyesThe content type's ID (not its slug). Look it up with contentType(slug:).
titleyesThe entry's title.
slugyesUnique within the content type.
fieldsyesObject keyed by field slug. Pass {} for none. Fields you leave out get their default value, if the field has one.
statusnodraft (default), published or archived. published sets publishedAt.
languagenoDefaults 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 success
4 error
5 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 success
4 error
5 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 success
4 message
5 error
6 }
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 success
4 error
5 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 success
4 error
5 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 success
4 message
5 error
6 }
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});
7
8await graph.graphql.updateContentEntry(entry.id, { status: 'published' });
9await graph.graphql.deleteContentEntry(entry.id);

See JavaScript SDK.