Skip to content

Pages

Page management endpoints. Pages are static content entries such as "About Us", "Contact", or "Services". Unlike posts, pages support hierarchical structure through parent-child relationships and do not have categories or tags.

Base path: /api/v1/pages


List Pages

GET /api/v1/pages

Required permission: pages:read

Query parameters

Parameter Type Default Description
skip int 0 Pagination offset
limit int 100 Pagination limit
status string Filter by status: draft, published, archived

Response 200 OK

[
  {
    "id": "uuid",
    "slug": "about-us",
    "status": "published",
    "order": 0,
    "parent_id": null
  }
]


Get Page Tree

Returns the full page hierarchy as a nested tree structure. Intended for frontend navigation — no authentication required.

GET /api/v1/pages/tree

Authentication: Not required

Response 200 OK

[
  {
    "id": "uuid",
    "slug": "services",
    "status": "published",
    "order": 0,
    "parent_id": null,
    "translations": [
      {
        "id": "uuid",
        "page_id": "uuid",
        "language_id": "uuid",
        "title": "Services",
        "content": null,
        "excerpt": null,
        "meta_title": null,
        "meta_description": null,
        "created_at": "2026-01-01T00:00:00Z",
        "updated_at": null
      }
    ],
    "children": [
      {
        "id": "uuid",
        "slug": "web-development",
        "status": "published",
        "order": 0,
        "parent_id": "uuid",
        "translations": [
          {
            "id": "uuid",
            "page_id": "uuid",
            "language_id": "uuid",
            "title": "Web Development",
            "content": null,
            "excerpt": null,
            "meta_title": null,
            "meta_description": null,
            "created_at": "2026-01-01T00:00:00Z",
            "updated_at": null
          }
        ],
        "children": []
      }
    ]
  }
]

Info

The tree is sorted by the order field at every level.


Create Page

POST /api/v1/pages

Required permission: pages:create

Request body

{
  "slug": "about-us",
  "status": "draft",
  "parent_id": null,
  "order": 0,
  "featured_image_id": "uuid",
  "author_id": "uuid",
  "translations": [
    {
      "language_id": "uuid",
      "title": "About Us",
      "content": "<p>We are a company...</p>",
      "excerpt": "Learn more about our company.",
      "meta_title": "About Us - CMS",
      "meta_description": "Learn more about our company and our mission."
    }
  ]
}

Field Type Required Constraints
slug string 2–255 characters, unique
status string draft, published, archived. Default: draft
parent_id UUID Must exist in DB
order int Default: 0, min: 0
featured_image_id UUID Must exist in media
author_id UUID Defaults to current user
translations array List of translations

Response 201 Created — full PageResponse object.

Errors

Status Description
404 Parent page not found
404 Featured image not found
409 Page slug already exists

Get Page

GET /api/v1/pages/{id}

Required permission: pages:read

Response 200 OK

{
  "id": "uuid",
  "slug": "about-us",
  "status": "published",
  "parent_id": null,
  "order": 0,
  "featured_image_id": "uuid",
  "author_id": "uuid",
  "created_by": "uuid",
  "translations": [
    {
      "id": "uuid",
      "page_id": "uuid",
      "language_id": "uuid",
      "title": "About Us",
      "content": "<p>We are a company...</p>",
      "excerpt": "Learn more about our company.",
      "meta_title": "About Us - CMS",
      "meta_description": "Learn more about our company and our mission.",
      "created_at": "2026-01-01T00:00:00Z",
      "updated_at": null
    }
  ],
  "featured_image": {
    "id": "uuid",
    "filename": "550e8400.jpg",
    "original_filename": "team-photo.jpg",
    "mime_type": "image/jpeg",
    "size": 204800,
    "path": "uploads/media/550e8400.jpg",
    "alt_text": "Our team"
  },
  "created_at": "2026-01-01T00:00:00Z",
  "updated_at": null
}

Errors

Status Description
404 Page not found

Get Page Children

Returns direct children of a page.

GET /api/v1/pages/{id}/children

Required permission: pages:read

Response 200 OK — list of PageSummary objects.

Errors

Status Description
404 Page not found

Update Page

PATCH /api/v1/pages/{id}

Required permission: pages:update

Request body — all fields optional

{
  "slug": "about-us-updated",
  "status": "published",
  "parent_id": "uuid",
  "order": 1,
  "featured_image_id": "uuid",
  "author_id": "uuid"
}

Response 200 OK — updated PageResponse object.

Status transitions

draft → published
draft → archived
published → draft
published → archived
archived → draft
archived → published

Errors

Status Description
400 Page cannot be its own parent
400 Circular page hierarchy is not allowed
404 Page not found
404 Parent page not found
404 Featured image not found
409 Page slug already exists

Delete Page

Soft-deletes a page. Cannot delete a page that has active children.

DELETE /api/v1/pages/{id}

Required permission: pages:delete

Response 204 No Content

Errors

Status Description
400 Cannot delete page with active children
404 Page not found

Warning

Delete or reassign all children before deleting a parent page.


Upsert Translation

Creates or updates a translation for a specific language. If a translation for the given language already exists, it will be updated.

PUT /api/v1/pages/{id}/translations

Required permission: pages:update

Request body

{
  "language_id": "uuid",
  "title": "About Us",
  "content": "<p>We are a company...</p>",
  "excerpt": "Learn more about our company.",
  "meta_title": "About Us - CMS",
  "meta_description": "Learn more about our company and our mission."
}

Field Type Required Description
language_id UUID Must exist in DB
title string Page title
content string Full page content
excerpt string Short summary
meta_title string SEO title
meta_description string SEO description

Response 200 OK — updated PageResponse object.

Errors

Status Description
404 Page not found
404 Language not found

Delete Translation

Removes a translation for a specific language from a page.

DELETE /api/v1/pages/{id}/translations/{language_id}

Required permission: pages:update

Response 200 OK — updated PageResponse object.

Errors

Status Description
404 Page not found
404 Translation not found

Create Section

Adds a new section to a page. Section data is validated against the schema for the given type.

POST /api/v1/pages/{id}/sections

Required permission: pages:update

Request body

{
  "type": "hero",
  "order": 0,
  "is_active": true,
  "reference_id": null,
  "translations": [
    {
      "language_id": "uuid",
      "data": {
        "title": "Welcome",
        "subtitle": "We build great things",
        "cta_text": "Learn more",
        "cta_url": "/about"
      }
    }
  ]
}

Field Type Required Description
type string Section type — see supported types below
order int Display order. Default: 0, min: 0
is_active bool Default: true
reference_id UUID ⚠️ Required for slider, gallery, form types
translations array List of translations with language-specific data

Supported section types

Type data fields reference_id
wysiwyg content
hero title, subtitle, cta_text, cta_url
cta title, subtitle, button_text, button_url
faq items[]{question, answer}
testimonials items[]{name, position, company, text}
gallery gallery_id
slider slider_id
form form_id

Response 201 CreatedPageSectionResponse object.

Errors

Status Description
400 Invalid data for section type
400 reference_id required for this section type
404 Page not found
404 Language not found

Update Section

PATCH /api/v1/pages/{id}/sections/{section_id}

Required permission: pages:update

Request body — all fields optional

{
  "order": 1,
  "is_active": false,
  "reference_id": "uuid",
  "translations": [
    {
      "language_id": "uuid",
      "data": {
        "title": "Updated title"
      }
    }
  ]
}

translations is an optional list — if provided, each entry is upserted by the section_id + language_id combination. Existing translations for a given language will be updated; if no translation exists for that language, a new record will be created.

Response 200 OK — updated PageSectionResponse object.

Errors

Status Description
400 Invalid data for section type
400 Section type does not use reference_id
404 Page not found
404 Section not found on this page
404 Language not found

Delete Section

DELETE /api/v1/pages/{id}/sections/{section_id}

Required permission: pages:delete

Response 204 No Content

Errors

Status Description
404 Page not found
404 Section not found on this page

Reorder Sections

Updates the order field for multiple sections in a single request.

PATCH /api/v1/pages/{id}/sections/reorder

Required permission: pages:update

Request body

{
  "sections": [
    { "id": "uuid", "order": 0 },
    { "id": "uuid", "order": 1 },
    { "id": "uuid", "order": 2 }
  ]
}

Response 204 No Content

Errors

Status Description
404 Page not found
404 Section not found on this page

Get Section Types

Returns all available section types with their data schemas. Intended for frontend form generation — no authentication required.

GET /api/v1/pages/section-types

Authentication: Not required

Response 200 OK

[
  {
    "type": "wysiwyg",
    "label": "Rich Text",
    "requires_reference": false,
    "data_schema": {
      "content": "string"
    }
  },
  {
    "type": "hero",
    "label": "Hero Banner",
    "requires_reference": false,
    "data_schema": {
      "title": "string",
      "subtitle": "string?",
      "cta_text": "string?",
      "cta_url": "string?"
    }
  },
  {
    "type": "cta",
    "label": "Call to Action",
    "requires_reference": false,
    "data_schema": {
      "title": "string",
      "subtitle": "string?",
      "button_text": "string?",
      "button_url": "string?"
    }
  },
  {
    "type": "faq",
    "label": "FAQ",
    "requires_reference": false,
    "data_schema": {
      "items": "[]{question: string, answer: string}"
    }
  },
  {
    "type": "testimonials",
    "label": "Testimonials",
    "requires_reference": false,
    "data_schema": {
      "items": "[]{name: string, position: string?, company: string?, text: string}"
    }
  },
  {
    "type": "gallery",
    "label": "Gallery",
    "requires_reference": true,
    "data_schema": {}
  },
  {
    "type": "slider",
    "label": "Slider",
    "requires_reference": true,
    "data_schema": {}
  },
  {
    "type": "form",
    "label": "Form",
    "requires_reference": true,
    "data_schema": {}
  }
]

Info

requires_reference: true means the section requires a reference_id pointing to an existing resource (gallery, slider, or form).