GraphQL Pages

Pages form a nested tree. A PagibleAI installation can have several root pages for different domains or languages, and each page stores immutable versions so editors can work on drafts without changing the public site.

Query root pages and children

The pages query is paginated. Filter by parent_id: null for roots or pass a parent UUID for its direct children.

query PageTreeLevel($parent: ID, $first: Int!, $page: Int!) {
  pages(
    filter: { parent_id: $parent, lang: "" }
    publish: DRAFT
    trashed: WITHOUT
    sort: [{column: _LFT, order: ASC}]
    first: $first
    page: $page
  ) {
    data {
      id
      parent_id
      name
      title
      path
      status
      has
      latest { id published publish_at }
    }
    paginatorInfo {
      currentPage
      lastPage
      count
      total
    }
  }
}
{
  "parent": null,
  "first": 25,
  "page": 1
}
{
  "data": {
    "pages": {
      "data": [
        {
          "id": "018f0f9d-83b3-7d69-a1b2-6f2db5e40c35",
          "parent_id": null,
          "name": "Home",
          "title": "Home",
          "path": "",
          "status": 1,
          "has": 12,
          "latest": {
            "id": "0198d2c8-b887-7a31-9e96-1082195f6bb0",
            "published": false,
            "publish_at": null
          }
        }
      ],
      "paginatorInfo": {
        "currentPage": 1,
        "lastPage": 1,
        "count": 1,
        "total": 1
      }
    }
  }
}

has is the number of descendants below the page, not a Boolean. Use publish: DRAFT for the latest editor versions and publish: PUBLISHED for currently published versions.

Retrieve one page

query PageDetail($id: ID!) {
  page(id: $id) {
    id
    parent_id
    related_id
    lang
    path
    domain
    name
    title
    to
    tag
    theme
    type
    meta
    config
    content
    status
    cache
    restricted
    access
    editor
    changed
    parent { id title }
    children(first: 25) {
      data { id title path }
      paginatorInfo { total }
    }
    ancestors { id title path }
    latest { id published publish_at editor created_at }
  }
}

The access field requires access:view; restricted only tells you whether immediate rules exist. Relations to shared elements and files require their corresponding view permissions.

Page fields

Frequently used Page fields

Field
Type
Purpose
id
ID!
Page UUID
parent_id
ID
Immediate parent or null for a root
related_id
ID
Translation group identifier
lang
String!
ISO language code or empty default language
path
String
URL segment
domain
String
Domain assigned to a root page
name / title
String
Navigation label and document title
to
String
Redirect path or URL
tag / theme / type
String
Navigation tag, theme and template type
meta / config / content
JSON!
JSON-encoded structured page data
status
Int!
0 inactive, 1 visible, 2 hidden from navigation
cache
Int!
Frontend cache lifetime in minutes
has
Int!
Number of descendants
latest
Version
Latest draft or published version
changed
JSON
Conflict details after an overlapping save

Filter and sort pages

Effective PageFilter fields

Field
Type
Matches
id
[ID!]
Any listed page UUID
parent_id
ID
Immediate parent or null for roots
lang
String
ISO language code
domain / path
String
Routing fields
tag / theme / to / type
String
Page properties
status / cache
Int
Visibility or cache lifetime
editor
String
Last editor
any
String
Indexed text across page fields

The current resolver applies the fields listed above. Use any for page-name, title, metadata, configuration or content text. Sort with sort: [{column: TITLE, order: ASC}]; supported columns are ID, NAME, TITLE, EDITOR and _LFT.

Create a page draft

Omit both parent and ref to append a root page. Pass parent to append a child, or ref to insert before a sibling. The new page receives an unpublished version.

mutation AddPage($input: PageInput!, $parent: ID) {
  addPage(input: $input, parent: $parent) {
    id
    parent_id
    title
    latest { id published }
  }
}
{
  "parent": "018f0f9d-83b3-7d69-a1b2-6f2db5e40c35",
  "input": {
    "lang": "en",
    "path": "about-us",
    "name": "About",
    "title": "About our company",
    "status": 1,
    "cache": 5,
    "meta": "{\"meta-tags\":{\"type\":\"meta-tags\",\"data\":{\"description\":\"Meet the people behind our company.\"},\"files\":[]}}",
    "config": "{}",
    "content": "[{\"id\":\"intro\",\"type\":\"text\",\"group\":\"main\",\"data\":{\"text\":\"This is our story.\"}}]"
  }
}

The MLL JSON scalar accepts JSON-encoded strings. Stringify meta, config and content once when sending variables, and parse those fields when reading them. References inside the decoded documents are resolved automatically, so addPage has no separate files or elements arguments.

Create a translation

Create a separate page for each language and set related_id to the source page’s UUID. Place the translation below the appropriate language root.

mutation AddTranslation($input: PageInput!, $parent: ID!) {
  addPage(input: $input, parent: $parent) {
    id
    related_id
    lang
    path
    latest { id published }
  }
}
{
  "parent": "018f3112-b0a3-7201-9040-1ed12f57fc20",
  "input": {
    "related_id": "018f0f9d-83b3-7d69-a1b2-6f2db5e40c35",
    "lang": "de",
    "path": "ueber-uns",
    "name": "Über uns",
    "title": "Über unser Unternehmen",
    "status": 1,
    "content": "[]",
    "meta": "{}",
    "config": "{}"
  }
}

Save redirects and canonical URLs

Set to to a site-relative path or validated absolute URL when the page should redirect. Store canonical metadata as a keyed, canonical entry and encode the complete metadata document once:

{
  "id": "018f0f9d-83b3-7d69-a1b2-6f2db5e40c35",
  "latestId": "0198d2c8-b887-7a31-9e96-1082195f6bb0",
  "input": {
    "to": "",
    "meta": "{\"canonical\":{\"type\":\"canonical\",\"data\":{\"url\":\"https://www.example.com/about-us\"},\"files\":[]}}"
  }
}

Pass these variables to the existing SavePage operation. Use a non-empty to only for a redirect; clear it with an empty string when the page should render normally.

Move a page

mutation MovePage($id: ID!, $parent: ID, $before: ID) {
  movePage(id: $id, parent: $parent, ref: $before) {
    id
    parent_id
  }
}

Position arguments

Arguments
Result
id only
Append the page at root level
id + parent
Append below that parent
id + ref
Insert before that sibling
id + parent + ref
Move below the parent and before the sibling

Save with conflict detection

PageInput is partial when saving. Send only changed fields, and pass the latest.id you originally read as latestId. If another editor saved first, PagibleAI merges non-overlapping changes and returns details in changed for conflicting fields.

mutation SavePage($id: ID!, $latestId: ID!, $input: PageInput!) {
  savePage(id: $id, latestId: $latestId, input: $input) {
    id
    title
    changed
    latest { id published }
  }
}
{
  "id": "018f0f9d-83b3-7d69-a1b2-6f2db5e40c35",
  "latestId": "0198d2c8-b887-7a31-9e96-1082195f6bb0",
  "input": {
    "title": "A clearer page title",
    "cache": 10
  }
}
{
  "data": {
    "savePage": {
      "id": "018f0f9d-83b3-7d69-a1b2-6f2db5e40c35",
      "title": "A clearer page title",
      "changed": null,
      "latest": {
        "id": "0198d3ad-5c8f-7be7-a09e-5ae6d4eac542",
        "published": false
      }
    }
  }
}

Update several pages

bulkPage applies one partial input to up to 1,000 pages. Set descendants: true to include each selected page’s subtree. Bulk saves create drafts; they do not alter published output.

mutation HideSubtree($ids: [ID!]!) {
  bulkPage(id: $ids, input: {status: 2}, descendants: true) {
    ids
    latest
    data
    failed
  }
}

Set frontend access

Access changes take effect immediately and are separate from page versions. Pass null for public access, an empty list to require any authenticated user, or one or more access values when a matching grant is required.

mutation ProtectPages($ids: [ID!]!) {
  setPageAccess(
    id: $ids
    access: ["customer", "subscriber"]
    descendants: true
  )
}
{
  "data": {
    "setPageAccess": 3
  }
}

Publish now or schedule

Publish the latest version of one or more pages with pubPage. Omit at to publish now or pass a DateTime to schedule publication.

mutation PublishPages($ids: [ID!]!, $at: DateTime) {
  pubPage(id: $ids, at: $at) {
    id
    title
    latest { id published publish_at }
  }
}

Clear cached page output

clearCache(id: ID!) invalidates the selected page and its descendants and returns the number of affected pages.

mutation ClearPageCache($id: ID!) {
  clearCache(id: $id)
}

Trash, restore and purge

mutation PageLifecycle($trash: [ID!]!, $restore: [ID!]!, $purge: [ID!]!) {
  dropped: dropPage(id: $trash) { id deleted_at }
  restored: keepPage(id: $restore) { id deleted_at }
  purged: purgePage(id: $purge) { id }
}

dropPage soft-deletes pages so they can be restored with keepPage. purgePage permanently removes them and cannot be undone. Keep destructive operations separate in production workflows so the intended IDs are easy to review.

Troubleshoot page operations

Common page problems

Symptom
Likely cause
Resolution
A list filter has no effect
The field is declared but not applied by the current resolver
Use the effective-filter table and any for indexed text
changed is not null
Another editor saved after the supplied latestId
Re-read the page, review overwritten fields and retry
Draft is not public
savePage never publishes
Review the latest version, then call pubPage
Scheduled page is not live
The publication time has not been processed
Check publish_at and the scheduled publish command
Invalid structured data
meta or config lacks type, data or files
Send canonical keyed entries as one JSON-encoded string
References are missing
An ID inside content or metadata is invalid or inaccessible
Check the referenced element/file and the caller’s view permission
A page remains restricted
Access rules are immediate and separate from versions
Call setPageAccess explicitly with null, an empty list or the intended values

Version fields

Version

Field
Type
Meaning
id
ID!
Version UUID
versionable_id
ID!
Owning page, element or file UUID
versionable_type
String!
Owning model class
lang
String
Version language
data
JSON!
Versioned model fields
aux
JSON
Page content/meta/config or file auxiliary data
published
Boolean!
Whether this version is published
publish_at
String
Scheduled publication time
editor
String!
Editor who created the version
created_at
String!
Version creation time