Scripto docs
View as Markdown

Articles

What an article is in Scripto — its fields, its draft/published lifecycle, per-user isolation, and the derived content invariant.

An article is one story. It is the top-level unit you create, edit, publish, and import. Under the hood it is a database row plus an ordered list of sections.

Fields

FieldTypeNotes
idUUIDThe article’s stable id. Used in every CLI command and API route.
titlestringDerived server-side from the first heading of the body.
subtitlestring | nullThe dek shown under the title; Medium reads it as the article subtitle.
premisestring | nullInternal orientation paragraph — the prose half of the Brief. Never published.
contentstringSemantic HTML. The derived concatenation of the sections’ HTML in order.
coverImagestring | nullFirst image in the body (denormalized for the list card).
wordCountintegerRecomputed whenever the body changes.
slugstring | nullAssigned at publish; the public URL segment. Null while a draft. Unique.
statusdraft | publishedA new article starts as draft.
createdAt / updatedAt / publishedAttimestampspublishedAt is stamped on first publish.

content is derived, never authored directly through the section path. The invariant content === concat(sections.html) holds on every write, so the editor and the public story page can keep reading the single content blob while the agent edits sections. See the sections model.

Lifecycle

Draft

scripto articles create --file draft.md (or POST /api/articles + a body update) creates a draft. Drafts have no slug and aren’t publicly reachable.

Published

scripto articles publish <id> assigns a slug once, sets status: published, stamps publishedAt, and returns the public /story/<slug> URL. Idempotent — re-publishing keeps the slug and original date.

There is no separate “archived” state; an article is a draft or published, and scripto articles (via DELETE /api/articles/[id]) removes it outright (its sections and notes cascade away).

Per-user isolation

Every article, section, and note row is scoped by userId. A request for an article you don’t own returns 404, never 403 — Scripto never leaks that an id exists. This holds identically whether you authenticate with a session cookie (web), a scripto_ Bearer key (CLI), or an OAuth token (MCP).

Create an article

scripto articles create --file draft.md

The server splits the Markdown into sections, renders each, and derives the title from the first heading.

Next