Skip to content
On this page

Post Types API

The Post Types API manages custom content type definitions (CPT) and pairs with the Blog posts API for typed custom_fields.

This page is written to help developers enable CPT safely and call the right routes quickly.

How to read this page

Each route is also documented as an operation page under Post Types operations:

  • what it does
  • who can call it
  • request shape
  • response and error codes

Base path

All CPT definition routes live under /api/v1/post-types.

Related Blog routes live under /api/v1/posts (?type=, body post_type / custom_fields).

Enable first

Installing the Module Hub schema does not turn CPT on.

bash
BLOG_ENABLED=true
POST_TYPES_ENABLED=true
# optional MCU: POST_TYPES_MCU=post-types://enabled

If disabled, every /api/v1/post-types call returns 503 with code: MODULE_DISABLED.

Install schema (after Blog). Prefer dry-run first, then run. Always send Content-Type: application/json and plain ASCII JSON (no rich-text / NBSP spaces from copy-paste).

http
POST /api/v1/modules/install/post-types/dry-run
Content-Type: application/json
X-Session-ID: <session>
json
{
  "target": "all",
  "runtime_scope": "flowless-core"
}
http
POST /api/v1/modules/install/post-types
Content-Type: application/json
X-Session-ID: <session>
json
{
  "target": "all",
  "runtime_scope": "flowless-core"
}

target: "all" installs core + ext-seo-per-type + ext-type-restrictions. target: "core" installs only core (ignores components). Empty body {} is valid and defaults to target: "core".

Error messageMeaning
Invalid JSON bodyBody failed JSON.parse (NBSP/smart quotes, bad Content-Type, truncated JSON)
Invalid input (+ fields)JSON parsed, but Zod rejected a field

See Module Install Dry Run.

Sample type definitions are documented on the Post Types Examples page — Module Hub never auto-installs them.

What the API is for

  • Listing and reading type definitions
  • Creating / updating / deleting types (admin)
  • Fetching public field_schema for admin UIs
  • Stats per type (admin)
  • Listing posts of a type via proxy

Route map

MethodPathAuthPurpose
GET/api/v1/post-typesPublicList types (?active=false includes inactive)
POST/api/v1/post-typesSession + CPT adminCreate a type definition
GET/api/v1/post-types/{key}PublicGet one definition
PUT/api/v1/post-types/{key}Session + CPT adminUpdate definition (type_key immutable)
DELETE/api/v1/post-types/{key}Session + CPT adminDelete (not built-in; not if posts exist)
GET/api/v1/post-types/{key}/schemaPublicSchema for editors (field_schema, behaviors, icon…)
GET/api/v1/post-types/{key}/statsSession + CPT adminCounts by status, lang, author
GET/api/v1/post-types/{key}/postsOptionalInternal proxy → GET /api/v1/posts?type={key}

Blog routes used with CPT

MethodPathCPT fields
GET/api/v1/postsQuery type (+ lang, filters)
POST/api/v1/postsBody post_type, custom_fields
PUT/api/v1/posts/{id}Body custom_fields (post_type locked)

See Blog operations.

Example: list types

http
GET /api/v1/post-types
json
{
  "data": [
    {
      "id": "ptd_1",
      "type_key": "case-study",
      "type_name": "Case Study",
      "field_schema": [],
      "behaviors": { "enables_scheduling": true },
      "is_active": true,
      "is_built_in": false,
      "sort_order": 100
    }
  ],
  "meta": { "total": 1 }
}

Example: create type

http
POST /api/v1/post-types
X-Session-ID: <session>
Content-Type: application/json
json
{
  "type_key": "case-study",
  "type_name": "Case Study",
  "description": "Customer success / case study",
  "slug_prefix": "case-studies/",
  "icon": "briefcase",
  "color": "#10B981",
  "is_active": true,
  "sort_order": 10,
  "field_schema": [
    {
      "name": "client_name",
      "label": "Client Name",
      "type": "text",
      "required": true,
      "max_length": 120
    },
    {
      "name": "industry",
      "label": "Industry",
      "type": "select",
      "required": false,
      "options": ["Tech", "Finance", "Healthcare", "Retail", "Education", "SaaS", "E-commerce"]
    },
    {
      "name": "challenge",
      "label": "The Challenge",
      "type": "textarea",
      "required": false
    },
    {
      "name": "solution",
      "label": "Our Solution",
      "type": "textarea",
      "required": false
    },
    {
      "name": "results",
      "label": "Results Achieved",
      "type": "textarea",
      "required": true
    },
    {
      "name": "duration",
      "label": "Project Duration",
      "type": "text",
      "required": false
    },
    {
      "name": "project_url",
      "label": "Live Project URL",
      "type": "url",
      "required": false
    },
    {
      "name": "technologies",
      "label": "Technologies Used",
      "type": "text",
      "required": false
    }
  ],
  "behaviors": {
    "public_by_default": true,
    "enables_seo": true,
    "enables_comments": false,
    "enables_reactions": true,
    "enables_bookmarks": true,
    "enables_views": true,
    "enables_scheduling": true,
    "requires_approval": true,
    "allows_password_visibility": false,
    "default_visibility": "public",
    "allowed_body_formats": ["markdown", "html", "tiptap_json"]
  }
}

Full sample catalog (7 types): Post Types Examples.

Example: schema for editors

http
GET /api/v1/post-types/case-study/schema

Returns field_schema, behaviors, icon, slug_prefix, color for dynamic forms.

field_schema item shape

json
{
  "name": "client_name",
  "label": "Client Name",
  "type": "text",
  "required": true,
  "max_length": 120,
  "placeholder": "ACME",
  "options": ["Tech", "Finance"]
}

Types: text | textarea | number | url | date | select | multi-select | boolean

Behaviors (common)

KeyEffect
requires_approvalNon-admins cannot publish
allows_password_visibilityAllow visibility=password
default_visibilityUsed when visibility omitted on create
enables_schedulingAllow scheduled_at / schedule
allowed_body_formatsRestrict body_format
max_per_authorCap posts of this type per author

Error codes

CodeWhen
MODULE_DISABLEDCPT env/MCU off (503)
USER_TYPES_DISABLEDPOST_TYPES_ALLOW_USER_TYPES=false
POST_TYPE_LOCKEDChanging post_type after create
APPROVAL_REQUIREDNon-admin publish when approval required
MAX_PER_AUTHORAuthor over type quota
CUSTOM_FIELDS_TOO_LARGE / validation errorsBad or oversized custom_fields

Env reference

VarDefaultNotes
POST_TYPES_ENABLEDfalseMaster switch
POST_TYPES_MCU``post-types://enabled@?...
POST_TYPES_REQUIRE_APPROVALfalseGlobal approval
POST_TYPES_DEFAULT_TYPEpostDefault post_type
POST_TYPES_ALLOW_USER_TYPEStrueAllow create via API
POST_TYPES_CUSTOM_FIELDS_MAX_KB10Cap
POST_TYPES_FIELD_SCHEMA_MAX_KB64Cap
POST_TYPES_ADMIN_ROLES(blog admin)Type CRUD + publish under approval
POST_TYPES_CACHE_TTL86400Def cache TTL
POST_TYPES_FRESH_CACHE_MODEtrueBypass Redis when true

Operation pages

Next