PHPackages                             skriva/statamic - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. skriva/statamic

ActiveStatamic-addon

skriva/statamic
===============

Statamic publish endpoints for Skriva: token-authenticated collection discovery and entry create/update.

v0.1.2(2w ago)011MITPHPPHP ^8.2CI passing

Since Jul 17Pushed 1w agoCompare

[ Source](https://github.com/getskriva/statamic)[ Packagist](https://packagist.org/packages/skriva/statamic)[ Docs](https://skriva.io)[ RSS](/packages/skriva-statamic/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (2)Versions (4)Used By (0)

Skriva — Statamic addon
=======================

[](#skriva--statamic-addon)

[![Packagist Version](https://camo.githubusercontent.com/3993f5c628a2b7b2b5fa8c727d0b74b6c753a9879c6a59a990d840518f1b5e2e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f736b726976612f73746174616d6963)](https://packagist.org/packages/skriva/statamic)[![CI](https://github.com/getskriva/statamic/actions/workflows/ci.yml/badge.svg)](https://github.com/getskriva/statamic/actions/workflows/ci.yml)[![License: MIT](https://camo.githubusercontent.com/7013272bd27ece47364536a221edb554cd69683b68a46fc0ee96881174c4214c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667)](LICENSE)

Statamic addon that lets [Skriva](https://skriva.io) publish generated articles into your collections. Statamic's REST and GraphQL APIs are read-only, so publishing needs a small addon on the Statamic side — the same shape as the [Payload plugin](https://github.com/getskriva/payload-plugin), speaking the same wire contract (`FieldNode` schema tree + mapped publish data). Block sets, SEO fields, relations and enums are discovered from your blueprint, never hardcoded.

Requirements
------------

[](#requirements)

- PHP 8.2+
- Statamic 5 or 6

Install
-------

[](#install)

From the Statamic project root:

```
composer require skriva/statamic
```

Then set the shared token in the Statamic site's `.env`:

```
AI_CONTENT_TOKEN=
```

Configuration
-------------

[](#configuration)

Optional, all via `.env`:

```
# Links leaving this site open in a new tab (default true). See /publish below.
AI_CONTENT_EXTERNAL_LINKS_NEW_TAB=false
```

Publish the config file with `php artisan vendor:publish --tag=ai-content-config` to edit the values directly.

Limitation: the new-tab treatment only applies to `bard` fields. A `markdown` fieldtype stores the markdown source verbatim and markdown has no `target` attribute — add the [`ExternalLink` extension](https://commonmark.thephpleague.com/extensions/external-links/) to your site's own markdown parser config if you want the same there.

Connect to Skriva
-----------------

[](#connect-to-skriva)

In Skriva, open your site's **Connection** page, choose destination "Statamic", paste the site's base URL and the same token, and pick a collection.

Endpoints return `503` while the token is unset — nothing is exposed without it.

Endpoints
---------

[](#endpoints)

All require `Authorization: Bearer ` and are rate-limited (60/min). No session, no CSRF.

### `GET /ai-content/manifest`

[](#get-ai-contentmanifest)

Connection test + collection discovery. Unlike Payload (plugin owns one target) this addon serves every collection; Skriva lists them and the user picks.

```
{
  "ok": true,
  "addon": "skriva/statamic",
  "statamic": "6.24.2",
  "meta": { "version": "0.2.0", "capabilities": ["draft_flow", "conditions", "defaults"] },
  "collections": [{ "handle": "landing", "title": "Landing pages", "dated": false, "hasRoute": true }]
}
```

`meta.version` is this addon's version; `meta.capabilities` are the behavior flags Skriva gates on (it never compares version numbers). The same `meta` rides along on `/schema`.

### `GET /ai-content/schema?collection=`

[](#get-ai-contentschemacollectionhandle)

The collection's default entry blueprint as a `FieldNode` tree — identical wire shape to the Payload plugin's `/schema`. Fieldtype mapping:

StatamicFieldNodebard, markdown`richText` (markdown slot; bard converted on publish)text, slug`text` — textarea → `textarea`integer, float, range`number` — toggle → `checkbox`, date → `date`select, radio, button\_group, checkboxes`select` with options (+ `hasMany`)replicator`blocks` (sets = block palette, grouped sets flattened)group`group` — grid → `array`entries, terms`relationship` + candidate options (cap 100, `cappedOut` above)assets`upload` + `isMedia` (never AI-filled)users, link, section, html, …omittedSets/groups whose required fields include media are flagged `unsatisfiable` so Skriva's compiler drops them instead of letting the AI attempt them. Reserved handles (`slug`, `date`, `parent`) are omitted — the entry's slug and date are managed by `/publish` itself.

Each field may also carry:

- `default` — the blueprint's default value. Skriva leaves such fields to Statamic instead of having the AI invent one.
- `condition` — a normalized form of `if`/`unless`/`if_any`/`show_when`/`hide_when`/`unless_any`: `{ negate?: true, when: "all"|"any", rules: [{ field, operator, value }] }`. The operator is split off the rule string (`"not published"` → `{operator: "not", value: "published"}`); a `custom fn:params` rule becomes `{operator: "custom"}`, which Skriva knows it cannot evaluate. Fields with `always_save: true` get no `condition` — Statamic stores them regardless.
- `requiredIf` — `required_if:field,value[,value…]` validate rules as `[{field, value}]`(multiple values are OR, as in Laravel).

### `POST /ai-content/publish`

[](#post-ai-contentpublish)

```
{
  "collection": "landing",
  "slug": "post-slug",
  "status": "published",
  "externalId": "optional-entry-id",
  "lenient": false,
  "data": {
    "title": "…",
    "intro": "…markdown…",
    "page_builder": [{ "blockType": "hero", "heading": "…" }],
    "seo": { "meta_title": "…", "meta_description": "…" }
  }
}
```

`data` follows the `/schema` semantics: richText slots carry GitHub-flavored markdown, replicator values are `{blockType, …fields}` rows. Conversion on write:

- bard ← markdown → HTML → ProseMirror via the field's own editor config (identical to a CP save), then degraded to what the field's `buttons` enable — the CP editor rejects the whole value on any node type its buttons don't cover. Disallowed leaves (horizontal rules) drop, disallowed containers (tables, lists, code blocks) unwrap to paragraphs, headings clamp to the nearest enabled level, disallowed marks strip. `markdown` fields store the source verbatim
- links in a bard value that leave this site get `target="_blank" rel="noopener noreferrer"` in the stored ProseMirror JSON, so a reader following a cited source keeps the article open. "External" is an absolute http(s) URL whose host (minus `www.`) is none of this installation's site hosts; relative paths, anchors, `mailto:`/`tel:` and Statamic references (`entry::…`) are never touched
- replicator rows become `{id, type, enabled, …}`; grid rows get ids; groups recurse
- entries/terms normalize to id arrays

Behavior:

- `externalId` present → updates that entry (404 if it no longer exists). Absent → looks the slug up in the collection and updates in place, else creates. Republishing never duplicates.
- Dated collections get `now()` as the entry date on create; `status: "draft"` saves unpublished. Statamic forces a required `date` field onto such collections, but it is a reserved handle that never reaches the request — the endpoint satisfies that rule on its own.
- The blueprint validates the converted values. Failures answer `422` with `"The following fields are invalid: a, b"` (`details` carries the full errors). Fields the request omits are validated against their blueprint default, so a required field with a default passes; the stored entry still omits it, leaving Statamic's default behavior in charge.
- `lenient: true` skips blueprint validation entirely, so an article Skriva cannot get past it is parked as a draft for you to finish in the control panel. Only allowed with `"status": "draft"`— combining it with `published` is refused (`422`). Request validation and the slug format still apply.

Response: `{ "ok": true, "id": "", "url": "", "editUrl": "", "created": true }`.

### `GET /ai-content/entry?id=`

[](#get-ai-contententryidentry-id)

Whether one entry is live yet — how Skriva notices that a parked draft was published by hand.

```
{ "ok": true, "id": "…", "published": true, "url": "https://…", "editUrl": "https://…/cp/…" }
```

`published` reflects the entry's real visibility, not just its published flag: a scheduled (future-dated) or expired entry answers `false`. `url` is null for collections without a route, and only meaningful while published — a draft's public URL 404s. A deleted entry answers `404`.

Development
-----------

[](#development)

```
composer install
vendor/bin/phpunit
```

Releasing
---------

[](#releasing)

The addon's version lives in `ServiceProvider::VERSION` (there is no `version` field in `composer.json` — releases are git tags). Bump that constant, add the CHANGELOG entry, then tag the commit with the same number; `meta.version` on `/manifest` and `/schema` must match the tag.

License
-------

[](#license)

[MIT](LICENSE)

###  Health Score

38

—

LowBetter than 82% of packages

Maintenance97

Actively maintained with recent releases

Popularity7

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity38

Early-stage or recently created project

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~13 days

Total

3

Last Release

19d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/26445454?v=4)[Skirva](/maintainers/Skirva)[@skirva](https://github.com/skirva)

---

Tags

contentpublishingstatamicStatamic addonskriva

### Embed Badge

![Health badge](/badges/skriva-statamic/health.svg)

```
[![Health](https://phpackages.com/badges/skriva-statamic/health.svg)](https://phpackages.com/packages/skriva-statamic)
```

###  Alternatives

[statamic/statamic

Statamic

830185.7k](/packages/statamic-statamic)[statamic-rad-pack/runway

Eloquently manage your database models in Statamic.

138249.0k8](/packages/statamic-rad-pack-runway)[statamic/seo-pro

73578.9k](/packages/statamic-seo-pro)[duncanmcclean/statamic-cargo

Comprehensive e-commerce addon for Statamic. Build bespoke e-commerce sites without the complexity.

3622.8k](/packages/duncanmcclean-statamic-cargo)[cboxdk/statamic-mcp

MCP (Model Context Protocol) server for Statamic CMS v6 — gives AI assistants structured access to content, blueprints, assets, and more.

3526.5k](/packages/cboxdk-statamic-mcp)[marcorieser/statamic-livewire

A Laravel Livewire integration for Statamic.

23133.7k17](/packages/marcorieser-statamic-livewire)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
