One engine, one door, every surface
Kernels, shredders, Enhance, arenas, seers, collections and Openers all work the same way: an endpoint with one configuration file, one call that runs it, a run row that records what it ran with, and a read-back that checks it.
Letterstory is built out of a small number of primitives: a writing kernel, a shredder, Enhance (the passes that add links, citations, visuals and metadata), an arena (research), a seer (a watcher), a collection (a publication that produces and publishes posts) and Openers (cold-email opening lines). Everything the product does is one of those seven, or an assembly of several. Each one is written up at /docs/primitives.
They are worth learning as a set, because they are deliberately the same shape. Once you have called one, you can call the others without reading much.
The seven
| Primitive | What one run does | The call | Capability |
|---|---|---|---|
| Kernel | A brief in, a finished article out | run_kernel | kernel:write |
| Shredder | A document in, the same document out with every sentence rewritten | run_shred | shredder:write |
| Enhance | A written post in, the same post with links, citations, visuals and metadata | run_enhance_pass | article:write |
| Arena | A topic in, a sourced and fact-checked outline out | run_arena | arena:write |
| Seer | One poll of a watched source, deduped and judged | run_seer | seer:write |
| Collection | One whole post: sourced, written, enhanced, published, and the site rebuilt | run_collection | collection:write |
| Openers | A prospect in, a hand-approved cold-email opening line out | run_lede | lede:write |
A collection run composes an arena, a kernel and the Enhance passes, which is why it takes the longest and why its own read-back quotes theirs.
The same five ideas, every time
1. An endpoint is the thing you call. A kernel endpoint, a shredder endpoint, an arena, a seer, a collection: each is a named row in your organization with its own public id, and that id is what appears in the URL (POST /api/kernel/ingest/{id}, /api/shred/ingest/{id}, /api/arena/ingest/{id}, /api/collection/ingest/{id}). Kernel endpoints, shredder endpoints and arenas are provisioned for you the first time you use one without naming it, so you never have to create an endpoint before your first call. Seers and collections you create yourself, because each one stands for a specific source or a specific publication.
2. Its configuration is one file. Not a scattering of settings across pages. get_*_config returns one document; set_*_config takes that same document back. What you read is exactly what you may write, so the workflow is read it, edit it, send it.
letterstory kernel endpoint config <id> > kernel.json
# edit kernel.json
letterstory kernel endpoint config <id> --file kernel.json
The endpoint files replace wholesale (a field you leave out is cleared). The collection file patches (a section you leave out is kept), because it is the configuration of a live publication rather than a call template.
3. A call sets constraints; a constraint beats a default. Everything you can set on an endpoint's defaults you can also set per call, and the call wins, field by field. So an endpoint is "how this pipeline usually runs" and a call is "except this time".
4. Every run records what it ran with. The run row carries constraints: the effective options after the defaults were merged in, as one document. "What did this run actually run with" is one read, not an investigation. Where a value could have been inherited from your organization's own settings, the row says which fields you declared and which it inherited.
5. Every run reads itself back. The run row also carries verification: a short list of checks, each pass, fail or skipped, that reads the constraints back against what came out. Each primitive's checks are listed at /docs/primitives. A collection run's checks quote the arena run's and the kernel run's own read-backs, so one read tells you which layer a bad post came from.
Two rules make verification worth reading. A check that did not apply is skipped, never quietly reported as a pass. And a failure is recorded and returned, never hidden, and never blocks: what to do about it is your pipeline's decision, not ours.
Who is billed: direct against internal
Every run is stamped with an origin.
- direct: you asked this door. It counts against your balance.
- internal: another part of Letterstory asked it on your behalf, because you told that product to run. Auto-Pen's research and writing, a collection's cadence, a phantom's launch. Recorded in your telemetry, billed to that product, not to you.
That is why a hosted post that researches, writes and shreds does not spend three separate balances, and why turning metering on for a primitive changed nothing for the pipelines already using it.
| Balance | On the house | Spent by |
|---|---|---|
get_kernel_run_balance | 10 runs | run_kernel, the Kernel Studio |
get_shred_balance | 10 shreds | run_shred, the Shredder Studio |
get_arena_run_balance | 50 runs | run_arena, the Arenas Studio, the post's Research panel |
get_seer_run_balance | 50 runs | run_seer, a dry run, the run-now button |
get_lede_balance | 10 ledes | run_lede, matched runs only |
get_phantom_balance | 3 sites | A hosted site being live, not its posts |
Collections are the exception that proves the rule: a phantom is sold as a site that keeps running, so what is metered is sites live at once, and each site's throttle is its weekly cadence. A post you ask for spends that week's cadence budget and is refused past it, rather than spending a separate credit.
A run that errors is free, everywhere.
Every surface is the same function
The app, an agent over MCP, plain HTTP, and the CLI are four projections of one function with one schema. There is no capability the app has that an agent does not, and the argument names are the same everywhere:
| Surface | Kernel | Shredder | Arena | Collection |
|---|---|---|---|---|
| MCP | run_kernel | run_shred | run_arena | run_collection |
| HTTP | /api/kernel/ingest/… | /api/shred/ingest/… | /api/arena/ingest/… | /api/collection/ingest/… |
| CLI | kernel write | shredder run | arena run | collection run |
| App | Kernel Studio | Shredder Studio | Arenas Studio | The Control Room |
This is enforced, not merely intended: the API reference at /docs is generated from the same manifest the tools are, and a build fails if the CLI stops sourcing a field a tool declares.
Long runs
Everything above except a short shred is submit-then-poll. Start it, keep the run id, poll it. See KB-405 for the pairs and how long each takes.
Documents
Anywhere you can hand Letterstory a document (a Studio, the CLI's --file, Strategy context, extract_document over MCP) the same list of formats is accepted: Markdown, plain text, Word, PDF, Rich Text, OpenDocument, HTML, CSV and TSV, and WebVTT or SubRip captions. Images embedded in a document are kept where the format lets us reach them, and where it does not, the refusal says so and names the export that would work instead of failing silently.