Single source of truth for Birkly CMS documentation

Build Birkly plugins as self-contained packages with plugin.json metadata, a PHP entry point, and declared permissions. The marketplace and CMS use these declarations for preflight checks and install consent.

A Birkly plugin lives in plugins/{folder}/ with at minimum plugin.json and plugin.php. Declare permissions, hooks, and data_access in plugin.json so site owners see an accurate consent screen at install. Register admin pages, API routes, field types, and automation actions from plugin.php. Package as a zip for marketplace upload or manual install.

Beginner

Minimum package

my-plugin/
  plugin.json    ← name, version, description, permissions
  plugin.php     ← registers hooks, routes, services
  admin/         ← optional admin UI pages
  api/           ← optional API endpoints

plugin.json essentials

FieldPurpose
nameDisplay name in admin
versionSemver (for example 1.0.0)
descriptionShort summary
requires_birklyMinimum CMS version
requires_phpMinimum PHP version
permissionsAdmin capabilities the plugin needs
hooksEvent names the plugin listens to
data_accessRead/write scope for entries, media, users, settings

Permissions and data access

Site owners see your declarations before install. Request only what you need:

  • permissions — strings like read_entries, manage_plugins, manage_settings.
  • hooks — event names like entry.saved, user.login.
  • data_access — per domain (entries, media, users, settings), set level to none, read, or read_write.

Example:

{
  "name": "My Integration",
  "version": "1.0.0",
  "description": "Syncs entries to an external CRM",
  "requires_birkly": ">=0.1.0",
  "requires_php": ">=8.1",
  "permissions": ["read_entries", "manage_settings"],
  "hooks": ["entry.saved"],
  "data_access": {
    "entries": "read",
    "media": "none",
    "users": "none",
    "settings": "read"
  }
}

What plugins can add

  • New admin pages and sidebar items
  • API endpoints under /api/{plugin_handle}/...
  • Field types in the collection editor
  • Automation triggers and actions
  • Template functions for the storefront

Local development

Place your folder in plugins/ and enable it in Settings → Plugins. Use scaffold-plugin.php to generate a starter package — see Plugin scaffolding CLI.

Publishing

Zip the plugin folder (no path traversal, no parent .. segments) and upload as a marketplace release. Preflight validates plugin.json against the package schema.

Advanced Users

Entry point (plugin.php)

Register services on load. Typical patterns:

  • Navigation via plugin.json navigation block or programmatic sidebar registration. Set navigation.icon to a registry name (for example "gear") or inline SVG — see Plugin navigation icon.
  • Routes under api/ mapped to /api/{handle}/....
  • Hook callbacks registered against names declared in plugin.json.
  • Visitor website tools (WebMCP) via birkly_webmcp_register_tools() — a catalog of public-site actions for Discovery → WebMCP. This is not inbound Birkly MCP (register_plugin_mcp_tools()), which is for AI apps connected to the CMS admin. See Website tools (WebMCP).
  • Admin presentation shelf (P83) — admin.scripts[], dashboard mounts, notification formatters, WebMCP runtime packs, and hook subscriptions. See Admin plugin extension.

Capability enforcement

Runtime checks use has_plugin_capability($plugin_id, $capability, $scope). The plugin security denylist in Settings → Security can hard-block collections or admin spaces regardless of granted capabilities — see Plugin security denylist.

Permissions manifest at install

The CMS resolves permissions_manifest from the marketplace release metadata or by previewing the zip's plugin.json. Consent is stored in settings/marketplace_consent.json keyed by {slug}/{version}. New versions with changed declarations require re-consent.

Dependencies

Declare soft or hard dependencies in plugin.json (requires_plugins). The CMS blocks activation when dependencies are missing — for example Events soft-depends on Commerce for paid tickets.

Package validation

Marketplace preflight checks:

  • Valid semver and required fields
  • Zip structure (single root folder or flat layout per schema)
  • No dangerous PHP patterns (eval, shell_exec, etc.)
  • Capability declarations present for plugins that access data

Developer tooling

ToolPurpose
scaffold-plugin.phpGenerate plugin skeleton
plugin-package.schema.jsonValidate local plugin.json
Birkly test suiteDenylist and capability tests

Styling your admin pages

Plugin admin screens should look native. The recommended path ships no CSS at all: wrap the screen in .plugin-page, emit breadcrumbs with {plugin_page_header}, and compose the rest from core classes (.card, .btn, .field, .table, .badge, .callout, .tabs, .modal, .empty-state) plus the u-* helpers. That inherits dark mode, focus rings, contrast-checked colours and reduced-motion handling for free.

If you do ship a stylesheet, load it from the top of your admin template:

<link rel="stylesheet" href="{birkly_url '/admin/css/plugin-page.css'}">
<link rel="stylesheet" href="{birkly_url '/plugins/{handle}/admin/{handle}.css'}">

and follow four rules: never restyle a core class, use var(--…) tokens rather than hex (hardcoded hex is broken in dark mode), no !important against core, and namespace every selector as .{handle}-*.

Full guidance — the three adoption tiers, the CI-verified public class contract, the legacy token alias table, a complete copy-pasteable Tier 0 page, and the .fieldtype-* API — is in Plugin admin styling.