Loopwise Docs
Admin APIMutations

Post Mutations

Mutation operations for managing posts in your Loopwise school

The post mutations allow you to create, update, and delete posts within your Loopwise school. These operations enable you to programmatically manage your blog content and article publishing.

Available mutations:

  • createPost — Creates a new post
  • updatePost — Updates an existing post
  • deletePost — Deletes a post (soft delete)

All post mutations return an errors field in the response that will contain any validation or processing errors that occurred during the operation.

Post Access Types

Posts support different access types:

  • login_required — Users must be logged in to access
  • paid — Users must have purchased access
  • public_access — Available to all users

Body Content Format

The body field is an HTML string. Posts are also editable in the school dashboard with a rich text editor, which supports standard article markup: headings (h1h6), paragraphs, lists, blockquotes, links, images, tables, and code blocks.

Compatibility note: if you write HTML through the API that the dashboard editor does not recognize (custom tags, inline scripts, unusual attributes), it may be normalized or removed the next time someone edits the post in the dashboard. Stick to standard article markup for content that will be co-edited in the dashboard.

To embed images inside the body, upload them first with the uploadImage mutation and use the returned URL in an <img> tag.

Cover Photos

Set a cover photo by passing coverPhotoUrl in the input of createPost or updatePost:

  1. Upload the image with the uploadImage mutation.
  2. Pass the returned image URL as coverPhotoUrl.

Only URLs served from the Loopwise CDN (as returned by uploadImage) are accepted; other hosts are rejected with UPLOAD-008.


Create a Post

The createPost mutation allows you to create a new post in your Loopwise school with the specified properties.

Input Parameters

FieldTypeDescription
inputAdminPostInput!Input object containing post creation details

AdminPostInput Fields

FieldTypeRequiredDescription
titleStringYesPost title
subtitleStringNoPost subtitle
bodyStringYesPost body content as HTML (see Body Content Format)
excerptStringNoPost excerpt
slugStringYesURL slug for the post. Must contain only lowercase letters, numbers, and hyphens
accessTypeStringYesAccess type for the post (login_required, paid, public_access)
publishedBooleanNoWhether the post is published (default: false)
publishedAtIntNoUnix timestamp when the post should be published
categoryIdStringNoID of a post category (list them with the postCategories query)
tagList[String!]NoTags to associate with this post
noindexBooleanNoExclude this post from search engine indexing
userIdStringNoID of the user (author) for this post (default: school owner)
coverPhotoUrlStringNoCover photo URL returned by uploadImage (see Cover Photos)

title, body, slug, and accessType are declared as nullable in the schema (the same input type is shared with updatePost), but they are required when creating a post. Missing fields are rejected with error code POST-006.

Return Type

type AdminPostCreatePayload {
  # Array of error messages, if any occurred during the operation
  errors: [String!]

  # The created post object, null if operation failed
  post: AdminPost
}

Example

mutation CreatePost {
  createPost(input: {
    title: "Getting Started with GraphQL"
    subtitle: "A comprehensive guide for beginners"
    body: "GraphQL is a query language for APIs..."
    excerpt: "Learn the basics of GraphQL"
    slug: "getting-started-with-graphql"
    accessType: "public_access"
    published: true
    tagList: ["graphql", "tutorial", "api"]
  }) {
    post {
      id
      title
      subtitle
      slug
      accessType
      published
      tags
    }
    errors
  }
}

Sample Response

{
  "data": {
    "createPost": {
      "post": {
        "id": "post_12345",
        "title": "Getting Started with GraphQL",
        "subtitle": "A comprehensive guide for beginners",
        "slug": "getting-started-with-graphql",
        "accessType": "public_access",
        "published": true,
        "tags": ["graphql", "tutorial", "api"]
      },
      "errors": []
    }
  }
}

Common Errors

ErrorDescription
POST-006 Missing required field(s)title, body, slug, or accessType was not provided
Title cannot be emptyThe post title is required
Body cannot be emptyThe post body content is required
Slug already existsThe provided slug is already in use
Invalid access typeThe accessType must be one of: login_required, paid, public_access
Category not foundThe specified category ID does not exist
User not foundThe specified user ID does not exist

Update a Post

The updatePost mutation updates an existing post in your Loopwise school. It has partial-update (PATCH) semantics: only the fields you include in the input are changed, and omitted fields keep their current values. You can update a single field — for example the title — without resending the body.

Input Parameters

FieldTypeDescription
idString!ID of the post to update
inputAdminPostInput!Fields to change; omitted fields are left untouched

AdminPostInput Fields

All fields are optional on update.

FieldTypeDescription
titleStringPost title (cannot be set to null)
subtitleStringPost subtitle. Pass null to clear it
bodyStringPost body content as HTML (cannot be set to null; see Body Content Format)
excerptStringPost excerpt. Pass null to clear it
slugStringURL slug. Must contain only lowercase letters, numbers, and hyphens
accessTypeStringAccess type (login_required, paid, public_access)
publishedBooleanWhether the post is published
publishedAtIntUnix timestamp of the publish time (see note below)
categoryIdStringID of a post category. Pass null to detach the category
tagList[String!]Replaces the post's tags. Pass [] to clear all tags
noindexBooleanExclude this post from search engine indexing
userIdStringID of the user (author) for this post
coverPhotoUrlStringCover photo URL returned by uploadImage (see Cover Photos)

publishedAt follows published: unpublishing a post clears its publishedAt, and publishing a post without a timestamp sets publishedAt to the current time. It cannot be cleared independently while the post is published.

Return Type

type AdminPostUpdatePayload {
  # Array of error messages, if any occurred during the operation
  errors: [String!]

  # The updated post object, null if operation failed
  post: AdminPost
}

Example

Update only the title and tags — every other field, including the body, is left untouched:

mutation UpdatePost {
  updatePost(
    id: "post_12345"
    input: {
      title: "Advanced GraphQL Techniques"
      tagList: ["graphql", "advanced", "optimization"]
    }
  ) {
    post {
      id
      title
      subtitle
      slug
      accessType
      published
      publishedAt
      tags
      updatedAt
    }
    errors
  }
}

Sample Response

{
  "data": {
    "updatePost": {
      "post": {
        "id": "post_12345",
        "title": "Advanced GraphQL Techniques",
        "subtitle": "A comprehensive guide for beginners",
        "slug": "getting-started-with-graphql",
        "accessType": "public_access",
        "published": true,
        "publishedAt": 1687436400,
        "tags": ["graphql", "advanced", "optimization"],
        "updatedAt": 1687436500
      },
      "errors": []
    }
  }
}

Common Errors

ErrorDescription
Post not foundThe specified post ID does not exist
Title cannot be emptyThe post title is required
Body cannot be emptyThe post body content is required
Slug already existsThe provided slug is already in use by another post
Invalid access typeThe accessType must be one of: login_required, paid, public_access
Category not foundThe specified category ID does not exist
User not foundThe specified user ID does not exist

Delete a Post

The deletePost mutation allows you to delete a post from your Loopwise school. This is a soft delete operation, meaning the post data is retained but marked as deleted.

Input Parameters

FieldTypeDescription
idString!ID of the post to delete

Return Type

type AdminPostDeletePayload {
  # Array of error messages, if any occurred during the operation
  errors: [String!]

  # Boolean indicating whether the deletion was successful
  success: Boolean
}

Example

mutation DeletePost {
  deletePost(id: "post_12345") {
    success
    errors
  }
}

Sample Response

{
  "data": {
    "deletePost": {
      "success": true,
      "errors": []
    }
  }
}

Error Response

If the deletion fails, you might receive a response like this:

{
  "data": {
    "deletePost": {
      "success": false,
      "errors": ["Post not found"]
    }
  }
}

Common Errors

ErrorDescription
Post not foundThe specified post ID does not exist
Post already deletedThe post has already been deleted
Insufficient permissionsYou don't have permission to delete this post

Important Notes

  • This is a soft delete operation — the post data is retained in the system but marked as deleted
  • Deleted posts will not appear in regular queries
  • The post can potentially be restored by system administrators if needed
  • All associated data (comments, ratings, etc.) will also be hidden when the post is deleted

On this page