/
Developer
Get a key
Knowledge base
Start here
Context
Topics & Planner
Self-serve setup
Phantom
Back to app
Interface →
Capability →
The grid
REST
Plain HTTP · the publish loop
670 tools
Set up →
MCP
The full surface, for agents
670 tools
Set up →
CLI
letterstory, in your terminal
670 tools
Set up →
Content
Read, draft, and publish articles: the words themselves.
49
all tools
49
all tools
49
all tools
Collections
A publication with one config file. run_collection sources, writes, gates and publishes one post.
44
all tools
44
all tools
44
all tools
Canvas & visuals
Canvas style templates, semi-structured compositions, stickers, and table/venn visuals.
21
all tools
21
all tools
21
all tools
Flows
Run editing passes over an article and read the results back.
2
all tools
2
all tools
2
all tools
Seers
Point a seer at a repo, Notion, a spec URL or a news query and turn what it finds into drafts. Connect with a GitHub PAT (set_github_connection) or a Notion secret (set_notion_connection).
28
all tools
28
all tools
28
all tools
Arenas
Research agents that turn a topic into a sourced, fact-checked outline, alone or before a kernel writes.
22
all tools
22
all tools
22
all tools
Writing kernels
Submit a topic/brief to an external writing kernel and get a finished draft written back.
40
all tools
40
all tools
40
all tools
Connectors
Push finished articles out to Webflow, Framer, or Google Docs.
32
all tools
32
all tools
32
all tools
Phantomstory
Sites Letterstory hosts as independent publications: create, configure, add a domain, rebuild. Publish with run_collection.
34
all tools
34
all tools
34
all tools
Mailers
Outbound email. Mailers: domains, warm-up, reply forwarding, suppression. Prospects: lists from CSV or a description. Sequences: multi-step emails with a review queue.
58
all tools
58
all tools
58
all tools
Strategy
Positioning, competitors, and onboarding that steer the writing.
105
all tools
105
all tools
105
all tools
Insights
Search Console performance and The Story's momentum feed. Connect Search Console first in Settings → Integrations → Google Search Console (https://app.letterstory.com/settings?tab=integrations).
58
all tools
58
all tools
58
all tools
Shredder
Run outside content through shredder endpoints: each sentence is rewritten across LLM providers, structure untouched.
16
all tools
16
all tools
16
all tools
Ledes
Turn a prospect's context into scored cold-email opening lines from your editable library.
20
all tools
20
all tools
20
all tools
Landings
Landing pages and when to cite them. Collections with auto-mention link to them in new articles.
5
all tools
5
all tools
5
all tools
The Site
One model of a customer's whole marketing site keyed by host and path: map a host, walk the tree, read what was noticed, and propose, accept or dismiss changes.
37
all tools
37
all tools
37
all tools
Account
API keys, organization profile, branding assets, members, and pending invites.
99
all tools
99
all tools
99
all tools
The grid
Capability
40 tools
Writing kernels
Submit a topic/brief to an external writing kernel and get a finished draft written back.
All 40 tools
POST
search_kernels
Search kernels by name/title in the caller's organization. All words must match. Returns 8 results unless `limit` says otherwise (20 at most), with their app links.
kernel:read
POST
list_kernels
List the writing kernels this organization can run with run_kernel: the shared catalog plus any kernels this organization built itself. Each entry carries a `description` saying what that kernel writes like — pick on that, not on the name, which is an arbitrary codename (Alder, Aspen, Birch…). A null description means nobody has written one yet. `owned` says which are this org's own (editable with update_kernel/archive_kernel); `is_default` marks the org's currently selected kernel (set_org_writing_kernel).
kernel:read
POST
get_kernel_run_status
Get the status of an article-bound kernel run: one started with run_kernel and an `article_id` (its `kernel_job_id`). Actively polls the kernel server: when the run just finished, the produced article is written into the article's content (a new version, same as watching it complete in the editor) and the response comes back status done with applied: true. A done run also carries `verification` — the app's checks and, under `verification.service`, the service's record of which claims the article used, which sentences it could not tie to the input, which held blocks came back byte-identical, which citations and links carried — the same record get_kernel_endpoint_run returns for a standalone run.
kernel:read
POST
list_kernel_endpoints
The caller's kernel endpoints: callable URLs that turn a brief into a finished article. Each carries the kernel it writes with (null = follows the org's selected kernel) and its lifetime run count.
kernel:read
POST
get_kernel_endpoint_config
The endpoint's whole configuration as ONE document: its name, the kernel it writes with, the shredder it shreds in the loop against, and its standing run defaults (word budget, person, rounds, entity gate, kernel version pin, in-loop shred tuning — every field run_kernel accepts per call except the content ones). This is the contract: what you get back is exactly what set_kernel_endpoint_config accepts, so read it, edit it, replace it.
kernel:read
POST
extract_document
Turn a DOCUMENT into text you can submit. Send the file's raw bytes base64-encoded with its real filename — the extension decides how it is read — and get back plain text for `run_kernel`'s `input` or `run_shred`'s `text`. Reads Word (.docx), PDF, Rich Text (.rtf), OpenDocument (.odt), HTML, Markdown (.md/.markdown/.mdx), plain text, CSV/TSV and caption files (.vtt/.srt); .doc, .pages, .pptx and .xlsx are refused with the format to convert to instead. USE THIS RATHER THAN READING THE FILE YOURSELF for anything that is not already text: a .docx is a zip and a .pdf is a binary container, so reading either as UTF-8 yields bytes no model can use and a run spent on noise. Embedded base64 images are removed on the way through and reported in `notice` — a Google Docs or Notion markdown export inlines every screenshot at ~67KB, which no text pipeline can carry and which is enough on its own to push a short article past a door's character cap. Free: no credit, no model call, nothing stored.
kernel:read
GET
get_kernel_endpoint_run
Poll a run started with run_kernel and collect the article once it is finished. Statuses: 'running' (keep polling, 15 to 30 seconds apart), 'done' (the article is in the response, with `verification`: the app's checks and, under `verification.service`, the service's record of which claims the article used, which sentences it could not tie to the input, which held blocks came back byte-identical, which citations and links carried), 'error' (the reason is in 'error'). Distinct from get_kernel_run_status, which polls an ARTICLE-bound run (run_kernel with `article_id`, by its `kernel_job_id`).
kernel:read
POST
get_kernel_endpoint_run_steps
The round-by-round draft text for a run started with run_kernel — every prompt step's input and output, per iteration. The same trace the app's kernel detail page shows as its version-diff and rule-trip views. Use it to see WHY a run's article turned out the way it did, beyond get_kernel_endpoint_run's pass/fail-adjacent verification record. Empty on a run that isn't finished yet.
kernel:read
POST
get_kernel_endpoint_run_record
The round-by-round record of a run started with run_kernel, for reading WHY the article came out as it did: for every round, what the writer did (retries, a discarded draft), the claims gate (runs before 2026-10-01; null on index runs, whose claims assembly placed: `assembly.claims_mode` and `assembly.claims_index`), each system rule and data-driven rule (found, rewritten, refused, still in the text, skipped by the budget), the shred, its send-backs by rule and the check after it, the rescan, provenance and the quality score, plus the text at each stage (the draft, after the gates, after the shred); then where the live rules did not hold on the shipped text and why (`gaps`), what assembly did (held blocks, links, stray markers, invented figures), the cost, the rules applied at their versions beside the catalog now, and the build that wrote it. Large (the texts of every round). Read get_kernel_endpoint_run for the article and the verification record; read this to see the pipeline's work. `unread_round_keys` names round fields this read does not project yet.
kernel:read
POST
list_kernel_rules
The live rules every kernel run is held to, as the kernels service publishes them right now: every active and paused rule with its id, name, version, status, scope, detector kind and shred policy. A run's `gaps` and record name rules by these ids; a rule at a higher version here than on the run means the rule changed since the run.
kernel:read
POST
get_kernel_pipeline
The writing pipeline as the deployed kernels service runs it: every stage in order (the door, each round, once on the shipped round, and the app's two steps outside the kernel), what each does, when it runs, whether it writes and how far one act of it reaches (the whole article, a sentence, a shred unit, nothing), where it lives in the kernels code, and the build that serves the answer. Read from the service's own registry, which its tests hold to the code; compare `build` with a run's `build` to know whether that run was written by the pipeline described here.
kernel:read
POST
get_kernel_writers
Every writing model the kernels service accepts and what a run on it would be: the seats it fills (writer, repair, judge, decompose, content — the judge, decompose and content models are the platform's and never follow the writer), whether the service can call it, and which of your kernels (and the catalog's) name it as their `writer`. The model providers beyond the gateway and whether each one's key is set. Use before switching a kernel's writer, and to read which models are under test.
kernel:read
POST
get_kernel_file
A kernel's version as its kernel file: one complete JSON document saying how it writes — `writer` (the model), `voice` (text or named paragraphs), `register`/`humor`/`inquisitive`/`person`, `rules` (every check as id → repair, measure or off, the system checks among them: system-rarity with its thresholds, system-person, system-framing, system-figures, system-entities), `effort` (rounds, samples, floor) and `advanced`. The length and the shred are per run (your endpoint's defaults or the request), and the judge is the platform's, so they are not in it. The live version by default; `version` reads another one of your own kernel's (list_kernel_versions lists them). For the catalog's kernels and your own.
kernel:read
POST
get_kernel_offer
For a kernel your org copied from the catalog: whether the catalog kernel has a newer live version than the one your copy was made from, which fields of its file changed, and your kernel file with those changes applied over your own edits. Nothing changes your kernel until you accept (accept_kernel_offer). `offer: null` when there is nothing new.
kernel:read
POST
list_kernel_versions
Every version of one of your org's kernels, newest first, each with its status (live: what runs use; candidate: runs only when named; retired: history) and the fields it changed; the live file; the catalog kernel it was copied from, with where your live file differs from it; and any newer catalog version waiting as an offer. The kernel page's file panel, in one read.
kernel:read
POST
get_kernel_file_schema
The kernel file's JSON Schema, exactly as the kernels service validates it: every field, its type, its range and what it means. Read it before writing a file for save_kernel_file or create_kernel.
kernel:read
POST
check_prose
Check one article for the prose defects the tuning comparisons and the live scoreboard read (lib/evals/prose-shape.ts): brief text that leaked into it (SOURCE PAGES and the other reference blocks), heading layout (a title straight into a heading, empty sections, prose above the title), too many short paragraphs or too-long ones, a long opening, and semicolon or dash joins. Returns the counts, which limits the article is past, and the limits. Deterministic and free: no model reads the text.
kernel:read
POST
judge_paired
A blind paired read of two texts, paragraph by paragraph: which side an editor-model prefers on each aligned pair, the tally, and an editorial rubric (natural, cohesion, fidelity, 1 to 5) for each side. It answers 'is this prose better than that prose', never 'does it pass', and one read sits inside about 24 points in 100 of noise, so compare against a control before reading a difference. Costs one judge call per pair, plus two per pair for the rubric. Read-only: no model writes a word.
kernel:read
GET
list_kernel_endpoint_runs
Recent endpoint writes (telemetry) for the caller's org, newest first, with each run's run_id and status. Optionally filter to one endpoint. Use it to find runs in progress, and to recover when a run_kernel submit timed out before returning a run_id — the run may have been accepted (and charged) anyway, so look here before resubmitting. A list carries only a short preview of each input and no article; get_kernel_endpoint_run returns the full input as received and the article.
kernel:read
POST
get_kernel_run_balance
The organization's standalone kernel-run balance: granted, used, and available. A run_kernel call (and a run started in the Studio) spends one; a run that errors is free. Check this before starting a batch — running out mid-way fails the remaining calls rather than queueing them.
kernel:read
POST
list_kernel_studio_runs
The Kernels Studio's History: every Studio run, kernel-page test run and start_kernel_studio_run of your org, newest first, with who started it (source), its status and length. Filter to one kernel with kernel_id.
kernel:read
POST
get_kernel_studio_run
Poll one Studio run (start_kernel_studio_run's run.external_id, or any run in list_kernel_studio_runs): its status and, once done, the article. The same run is readable in full — input, constraints, verification — with get_kernel_endpoint_run, and round by round with get_kernel_endpoint_run_steps.
kernel:read
POST
suggest_kernel_voice_settings
Read a free-form note about a kernel's voice ("make it funnier", "more formal") and say which of the structured voice settings it asks for — register, humor, inquisitive — and what of the note is left over for voice_config.notes. Changes nothing: merge `patch` into the kernel's voice_config and save it with update_kernel. The kernel page runs this before saving its notes field.
kernel:read
POST
get_org_writing_kernel
Get the caller's organization's selected writing kernel — the kernel id and, if one is set, the full writing_kernels record.
kernel:read
POST
create_kernel_endpoint
Create a kernel endpoint for the caller's organization. Returns its callable public id; calls authenticate with the org's API key (POST /api/kernel/ingest/<public_id>). Bind a kernel_id to pin what it writes with, or omit it to follow whatever kernel the org has selected.
kernel:write
POST
set_kernel_endpoint_config
Replace the endpoint's whole configuration with one document — the same shape get_kernel_endpoint_config returns. Fields left out of `defaults` are cleared (this REPLACES; use create/update tools for field-by-field edits). The kernel must be one this org may run and the shredder endpoint must be this org's, exactly as on create.
kernel:write
POST
delete_kernel_endpoint
Delete one of the caller's kernel endpoints. Its past runs are preserved.
kernel:write
POST
add_kernel_custom_eval
Add (or replace-by-id) one custom eval on an org-owned kernel's rules.custom[] — your own check the kernels service runs alongside its built-in rules, on TOP of the always-on gates (em-dash, worn-AI-framing, word rarity) no config can turn off. Every genuinely new id is filed as a PROPOSAL only (status 'proposed') and never auto-activated, whether or not it has a repair — a person must review and activate it through the kernels service before it enforces on any article; this tool cannot skip that. Re-submitting an id the service already filed from an earlier call is a no-op THERE (see custom_evals.skipped in the response) — it only replaces this kernel's own draft declaration of that id, which is what lets you fix a typo before review. detector.kind: 'builtin' is not accepted; that is reserved for the service's own hand-wired gates.
kernel:write
POST
remove_kernel_custom_eval
Stop declaring one custom eval on an org-owned kernel — removes it from rules.custom[] going forward. Does NOT retire whatever the kernels service already filed or a person already approved for that id; that stays until a person acts on it through the kernels service's own review surface. Idempotent: removing an id that isn't currently declared succeeds without error.
kernel:write
POST
run_kernel
THE ONE CALL THAT WRITES. Give it `input` and it hands the text back; add `article_id` and it writes into that article instead (locked for the run, the collection's own voice and client-mention policy applied, the finished draft applied with its accuracy record). With `article_id` and no `input`, it writes from that article's current content. Write an article from one input field, `input` — a brief, an outline, notes, a research dump, or your own finished document to regenerate. PASS THE SOURCE ITSELF, never a summary you wrote of it: tables, links and inline citations are held and restored from what you send, so a digest of a document arrives with none of them. `content_kind` says which: 'instruction' (direction to write BY, still held to the content floor below), or 'article'/'outline'/'research'/'notes' (SOURCE MATERIAL, decomposed into a typed brief first and GENERATED FRESH, never edited in place — 'auto', the default, decides between 'instruction' and 'article' by length alone). Your own finished post is 'article'. On EVERY decomposed kind your tables, images, code, quotes and references are held byte for byte and restored in place in the generated draft, and every claim the article makes is checked against your input's own claims — you do not have to name a kind to get that. Shape is always preserved — sections are never moved — and your own `skeleton`, if you pass one, wins over the derived shape. A kernel writes prose from content it is given and does not source content, so source material submitted as `content_kind: 'instruction'` comes back as its own input. Pass shredder_endpoint_id (or bind one to the endpoint) to shred IN THE LOOP: every round is shredded before the kernel judges it, so the verdict lands on the text that ships, and the shred is logged against that shredder endpoint. That costs a shred as well as a run. You do not have to create a shredder endpoint yourself first: pass shred_aggression (or shred) with no shredder_endpoint_id and none bound, and the org's default shredder endpoint provisions on first use, the same way the org's default kernel endpoint does — it is then a real shredder endpoint, its runs show up in your own shred usage. Pass shred_aggression to control how hard that in-loop shred rewrites ('standard', the default and recommended, 'high', or 'max' — same scale run_shred uses). A HEADLINE OR BARE TOPIC IS NOT ENOUGH ON ITS OWN: `content_kind: 'instruction'` (or 'auto' landing there) submits the text as-is, and anything under roughly 150 words / 8 substantive sentences is refused before any model call rather than written — the kernel does not invent the mechanism, examples or figures a thin topic is missing. If you only have a sentence, ask for a few paragraphs of real material (an outline, notes, a rough draft) and submit that with the matching `content_kind`. Pass entities to name how specific things get treated — a client, a competitor, anything — as data instead of writing the instruction yourself: never (off-limits entirely), mention_once (one natural mention), mention_often (referenced a few times where relevant), or lead_company (named first, most prominent). Each also takes `linkable: false` (default true) to forbid a link or citation regardless of how often it's named — that's the old 'name_only' treatment, now a modifier rather than its own slot. Checked back on settle for never/mention_once/mention_often (frequency only; `linkable` and `lead_company` are prose-only). ASYNCHRONOUS AND SLOW: a kernel write commonly takes eight minutes or more, so this returns a run_id immediately and you poll get_kernel_endpoint_run until status is 'done' (or 'error'). The submit itself answers in seconds whatever the input's length — the service mints the run id first and reads the input (decompose included) behind it — and a resubmit of the same input and options within the service's window returns the run already started rather than a second charge, so if a submit times out, resubmit it or call list_kernel_endpoint_runs. `decomposed` on the response is the service's decision (null when it did not say). On 'done', get_kernel_endpoint_run's `verification` carries the app's checks and, under `service`, the service's own record: which claims the article used, which sentences it could not tie to your input, which held blocks came back byte-identical, which citations and links carried. Which kernel writes: kernel_id if you pass one, else the kernel bound to the endpoint, else the org's selected kernel. Without `article_id` this touches no article — the prose comes back to you.
kernel:write
POST
accept_kernel_offer
Accept the catalog's newer version into a kernel your org copied (get_kernel_offer shows it first): the catalog's changes are applied over your own edits and saved as a new version of your kernel. `status: candidate` (the default) keeps it out of your runs until you make it live (promote_kernel_version); `live` makes your endpoints run it from their next call.
kernel:write
POST
save_kernel_file
Save a kernel file as a new version of one of your org's kernels. Send the whole file you want (get_kernel_file, edit, send back) — only `name` and `voice` are required, and anything left out is completed with the platform's settings. `status: candidate` (the default) changes no run: try it with run_kernel's `kernel_version` (or start_kernel_studio_run), then promote_kernel_version. `live` makes your kernel's runs use it from their next call. A refused file names each failing path. Your own rules in `rules` are filed for review, never auto-activated. Every version is kept, so any change can be undone by promoting an earlier one. The schema: get_kernel_file_schema.
kernel:write
POST
promote_kernel_version
Make a stored version of one of your org's kernels live: ship a candidate you tested, or roll back to an earlier version. The version that was live is kept and returned as previous_live, so this is undone by promoting it again. Your kernel's runs use the new live version from their next call.
kernel:write
POST
create_kernel
Create a kernel your org owns, stored as version 1 (live) and handed back whole; nothing of ours changes under it later. Three starts, one call. From a file: as short as `{ "file": { "name": "Acme", "voice": "Write like our CTO talks: plain, specific, a little dry." } }` — the rest is completed with the platform's settings. From a kernel you can see: `{ "from": "<kernel id or name>", "file": { "name": "Acme v2" } }` copies its file (your org's current kernel, from get_org_writing_kernel, gives a new kernel that starts as your regular settings; any other is a fork that stays yours), with `file` laid over it. From your own skill: `{ "skill": "<the text of a SKILL.md or any markdown / plain-text writing instructions>" }` — its body becomes the kernel's `voice`, its frontmatter `name` and `description` name it, and everything else comes from `from` or else your org's current kernel. To copy a catalog kernel that keeps receiving our newer versions as offers, use configure_kernel. The schema: get_kernel_file_schema.
kernel:write
POST
configure_kernel
Make an org-editable COPY of a kernel from list_kernels. The shared catalog is one row per kernel across every organization, so a catalog kernel cannot be edited in place — configuring copies its config into a kernel this org owns, which can then be tuned. The copy records what it came from. Editing its rules is a dashboard action (/kernels); this tool creates the copy.
kernel:write
POST
update_kernel
Update an org-owned kernel's name, description, and/or voice (register/humor/inquisitive/notes) — the same fields the app's kernel detail page edits. Raw prompt/config editing stays staff-only, same as always. A kernel set up directly by Letterbrace (voice_config: null on the app's own kernel detail page) refuses voice_config here too — name/description still work for it.
kernel:write
POST
archive_kernel
Archive an org-owned kernel (never a hard delete — every run it ever produced keeps its attribution). It stops being offered for new runs; existing runs and articles are unaffected.
kernel:write
POST
start_kernel_studio_run
Try a kernel the way the Kernels page's Studio does: write a throwaway article from a brief with any kernel your org can use (the catalog's or your own), and keep it in the Studio's History, where your team sees it beside their own runs. Same door, floor, credits and options as run_kernel, including `kernel_version` to try a candidate version of your own kernel before promote_kernel_version. Spends one kernel run. Returns at once; poll with get_kernel_studio_run.
kernel:write
POST
update_kernel_endpoint
Rebind one of your kernel endpoints: which kernel it writes with and which shredder endpoint its runs shred against, in the loop. An explicit null unbinds (a kernel null follows your org's selected kernel; a shredder null stops shredding); a field you leave out is unchanged. For the endpoint's whole configuration, defaults included, use set_kernel_endpoint_config. Only the endpoint's creator or an org admin can change it.
kernel:write
POST
set_org_writing_kernel
Set (or clear, with null) the caller's organization's selected writing kernel. Errors if the kernel id doesn't exist. Selecting a global (shared, staff-curated) kernel requires the key's creator to be an organization admin — same rule the app enforces.
kernel:write