/
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
44 tools
Collections
A publication with one config file. run_collection sources, writes, gates and publishes one post.
All 44 tools
POST
search_collections
Search collections 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.
collection:read
POST
list_collections
List the caller's collections — each a publication with a standing configuration that produces and publishes posts on a cadence. `public_id` is the collection's address for the HTTP door (POST /api/collection/ingest/{public_id}); `hosted` marks a phantom (a site Letterstory hosts as an independent publication); `sponsor_disclosure_surfaces` says where it discloses its sponsor (empty = nowhere). Use collection_id to filter list_articles, as the target for ingest_article, or to run_collection.
collection:read
POST
get_collections_overview
How is each collection doing? The Collections page as data: every collection with the site Letterstory hosts for it (null for the client's own blog), where it publishes, its running state in one word (publishing / paused / setting_up / needs_attention / manual), posts total and published with the last publish date, posts showing in Google (hosted sites), and AI reads — AI assistants fetching its pages to answer someone — over the last 30 days with the 30 before and a daily series (hosted sites). For the collection's settings use get_collection_config; for posts, list_articles.
collection:read
POST
get_collection_config
Read a collection's configuration as ONE FILE: identity (brand_subject, publish_control), content (research depth, kernel family, verify/shred/visuals, cover, mention degree, template), cadence (on/off, posts per week, timezone, sourcing, approval mode, direction), publishing (publish locally, CMS, rebuild), targets (whether the org's ranking targets are pressed on this collection, the steering tier and the share of write slots it aims at target ground, what is bound), measurement (GEO probing), sourcing (demand_focus: whether headline ranking leans on search, AI answers, or both), disclosure (where an independent publication discloses its sponsor — the 'safe mode' surfaces — and who it names), and custom_fields (this collection's own declared post fields — e.g. faq, itemList, seoTitle — each generated automatically by run_custom_fields_pass). What set_collection_config patches, read back from the rows the engine actually reads.
collection:read
GET
get_collection_run
Poll a collection run: status (scheduled → sourcing → writing → ready → publishing → published; failed | skipped | cancelled), the step in flight, the headline, the article it wrote, the constraints it ran with, the verification read-back once settled, and the arena/kernel runs it composed. Polling nudges a run in flight forward. A run at 'ready' under manual approval waits for approve_collection_run.
collection:read
POST
list_collection_runs
Recent collection runs — the whole org, or one collection — newest first, with origin (direct vs the planner's own slots), caller, status, and verification. This is the planner's calendar too: 'scheduled' rows are its upcoming slots.
collection:read
POST
list_topics
List a collection's content-planner topic map: the pillars (clusters) it's building topical authority on, and each pillar's spokes (candidate subtopics) with their status. ONLY status 'open' is available to write: 'queued' means the Planner has already claimed it for a scheduled slot, 'covered' means a post already exists for it (see article_id), and 'dismissed' means it was rejected — writing any of those duplicates work the site has already done. Use an open spoke's topic_id with set_topic to queue it as the next piece, or with create_phantom_from_topic to write it now.
collection:read
POST
get_collection_context
Why is this collection writing what it's writing? Returns the context generation actually reads for it — every field resolved org → offering → collection, with origin naming the layer that decided each one — plus the collection's own layer (local, what set_collection_context edits) and its mention set (offerings; the primary is the offering it publishes as and inherits from). Context is layered org → offering → collection. Each layer stores ONLY what it overrides: an omitted field inherits, and null resets a field to inheriting. name, aliases, url, manifesto, identity, default_framing and priority_topics resolve to the nearest layer that sets them (priority_topics REPLACES, so a product never inherits its siblings' focus); stances, avoid_topics and blocked_mentions are ADDITIVE across layers, and stances rank most targeted first (collection, then offering, then company-wide). For the standing instructions steering it, see list_collection_directives.
collection:read
POST
list_automations
A letterbase's automations: what it writes, how often, and why (docs/architecture/automations.md). Each is one kind: topic (a pillar of the topic map, optionally aiming at a ranking target, or goal-led: the topics that press a target), series (a format repeated over items), signal (a seer: writes when something happens) or best (the most valuable idea anywhere: the rest of the week). weekly_total is the sum of the paces that are on. With none stored, `derived` lists what runs today; the first change stores them.
collection:read
POST
get_automation
One automation, opened: what it covers (scope: its subjects, items, watched settings or sources, each written or not), its scheduled posts and the open slots the engine will give it (soonest first: an open slot goes to the automation furthest behind its pace), the ideas it writes next in write-next order, and what it wrote last. The same read as the letterbase page's automation row, opened.
collection:read
POST
get_internal_link_status
The internal-link refresh for one collection: whether it is on (on by default wherever the collection has a sitemap), how many published posts are due for a refresh now, and when the next one comes due. The nightly sweep refreshes due posts, most overdue collections first, every post at least every two weeks; on a manual-approval or client-CMS collection the new links are proposed for approval (list_revisions) instead of republished. Run the next batch now with refresh_internal_links.
collection:read
POST
preview_preferred_sources
Step 1 of bringing a collection's PUBLISHED posts up to its preferred sources (update_auto_pen_config preferred_sources). Queues one DRY proposal per eligible post — published, with a Sources list, and a preferred site it does not cite yet. Each searches the preferred sites for pages on the post's topic, reads them, and proposes at most one sentence stating one of their figures, judged against the sources the post ALREADY cites: a fit from 1 to 5 and whether it adds anything they do not. Nothing is written to any post. The enhance cron works through the queue every two minutes; read the result with get_preferred_sources_preview. Supersedes an unfinished earlier preview.
collection:read
POST
get_preferred_sources_preview
Step 2: the latest preferred-sources preview of a collection, ranked. A proposal QUALIFIES at fit 4 or 5 when it adds something the post's sources do not. For the client's own site only the strongest qualifying proposals are chosen, up to the collection's ceiling (client_one_in: at most one in N published posts cites the client, counting those that already do); relevance decides how many, the ceiling only caps it. Qualifying third-party proposals are all chosen. Returns each proposal's sentence before and after, its link, fit and reason; what is held by the ceiling or below the bar; why nothing fit elsewhere; and `projected` — the posts citing the client after applying, of the published, as a 1-in-N.
collection:read
POST
get_collection_inventory
Where every piece in a collection is — being written, written but not live (and what each waits on), live, and the client's own imported posts kept apart, each listed post with its coverImage — plus pendingReview: finished drafts never published, each with autoPublishAt, when it goes live with nobody acting (the collection's Human Checkpoint decides: autopilot publishes on the next sweep, a review window when its hours close, manual approval never — null means it waits for a person). The same read as the collection room's Work tab and 'needs you' strip.
collection:read
POST
get_enhancer_file
The enhance file a collection's passes run, as ONE complete, versioned JSON file — the same pattern as a kernel, arena and shredder file: the model in each pass's seats (visuals: brief, draw, pick, table; internalLinks: rank, place; externalLinks: source, verify; metadata, and the model a shredded post's metadata uses) and each pass's settings (the internal-link ceiling, sitemap candidates, source types, citations per source, visuals per post). Whether a pass runs stays the collection's policy (update_auto_pen_config). Says where the file comes from (your own copy, a pinned platform version, or ours) and whether we have released a newer version than your copy's base (`offer`).
collection:read
POST
list_enhancer_versions
A collection's enhance file history: your copy's versions (newest first, the live one marked) and our released platform versions, which you can pin (pin_enhancer_version). Every pass run records the version it ran (get_enhance_run `version_label`).
collection:read
POST
create_collection
Create a collection — a publication with its own configuration, cadence and topic map. Pass `config` to start it configured (cadence, content, publishing, targets, measurement — the same one-file document set_collection_config takes); its identity (brand_subject, publish_control) defaults to a client blog publishing on Letterstory. For a site Letterstory hosts as an independent publication, use create_phantom instead. Returns the collection with its public_id.
collection:write
POST
set_collection_config
PATCH a collection's one-file configuration — send only the sections and fields you mean to set; everything omitted is kept. `cadence.enabled: true` turns the planner on and materializes its publish slots; `targets.enabled: true` binds the org's active ranking targets (add_ranking_target) to this collection so the tier's share of write slots press them (1 in 2, 2 in 3 or 4 in 5; a rung needs both cited pages and published target posts — targets.ramp) — the same share orders list_suggestions and the slot claim — at the tier it earns or the manual `targets.tier` you set. `content.mention_degree` must stay 'off' on an independent collection — the capability flag wins. `relevance` holds how tightly the collection stays on the client's product — min_topic_relevance, brand_gate, min_brand_fit, pillar_breadth, auto_approve_pillars, keyword_scope — each on its capability's preset (a client-brand main blog held to the product, an independent phantom free to cover its field) unless set here; null puts a dial back on the preset, `overridden` lists the ones set, and each change is logged. `sourcing.demand_focus` ('seo' | 'geo' | 'balanced') shifts headline-ranking weight toward search or AI-answer demand without dropping the other; ranking targets keep the steering tier's share of slots under every focus, and each change is logged with its actor. `disclosure` ('safe mode') sets where an independent publication discloses its sponsor: `surfaces` is the whole set (article_header, article_footer, cover_badge or cover_baked, structured_data, sponsored_links, meta_description, site_statement, site_byline; [] = off; safe mode = every surface except the cover ones; cover_baked serves cover_image as the cover with the label drawn in) and `sponsor_label` overrides the name (required on internal/phantom_farm orgs). It is served beside the body as sponsor_disclosure on list_published/get_published/get_article, never written into it, so a change reaches every existing post on the site's next rebuild (triggered here); a client-brand collection cannot disclose. `custom_fields` is the one array-shaped section: sending it REPLACES the whole declared set of this collection's own post fields (upsert-by-key, delete anything no longer listed) — each has a key, label, generation_prompt (the instruction handed to the model), a shape (text | list_of_text | object | list_of_objects), and write_policy ('always' regenerates every pass; 'create_only' is written once and never touched again). A field's generation_prompt is free text sent straight to the model and written into every future post — treat this capability accordingly when sharing the key that grants it. Values are generated by run_custom_fields_pass and appear on get_published/list_published as custom_fields.<key>. Returns the whole file as it now reads plus which sections were applied.
collection:write
POST
recheck_topic_relevance
Re-judge a collection's PENDING topic-map spokes (open and queued) against its relevance dials (get_collection_config `relevance`) — the same brand check the topic map runs on new topics, for a backlog planned before the dials changed. Under the collection's brand_gate: strict drops off-brand topics and flags unsure ones; narrow drops unsure too; lenient drops off-brand only; off judges nothing. A dropped OPEN topic is set to 'dismissed' with a brand_check evidence note saying why (reopen it to undo); a dropped QUEUED topic is only flagged, since it is already in a scheduled run. apply: false (default) returns the verdicts and writes nothing; apply: true writes them and logs the pass, with its counts and your note, to the collection's event history.
collection:write
POST
delete_collection
Delete a collection from the caller's organization. Articles in the collection are preserved but become orphaned (hidden from listings) until reassigned with assign_article_to_collection. A hosted phantom's collection cannot be deleted this way — tear the phantom down (delete_phantom).
collection:write
POST
set_main_collection
Make a client collection the organization's MAIN blog: the collection get_onboarding_plan treats as the main blog (its collection and planner steps target it), Measure defaults to, and the company's own-site AI-search probes and Search Console binding attach to. Exactly one per organization; the previous main blog stays as it is, just no longer main. Use it when the organization has more than one client collection and the plan points at the wrong one (list_collections shows `is_main`). Refuses a phantom (an independent publication). Idempotent: `changed` is false when it was already the main blog.
collection:write
POST
run_collection
Produce ONE POST on a collection, THE DOOR: source the topic (given, a spoke of the map, a pool headline, or the next open spoke), write it (research through an arena, the kernel, the link/visual/metadata passes, optional shred — all in-process), gate it (approval_mode: 'manual' pauses at 'ready' for approve_collection_run, 'review_window' publishes after the hold, 'autopilot' goes straight through), publish it by the collection's publish_control, mark the spoke covered, and — when the collection is a hosted phantom — rebuild the site. Every run gets a row (get_collection_run / list_collection_runs) recording the constraints it ran with and a verification read-back (sourced, written, independent, linked, illustrated, published, covered, rebuilt). Every field is optional and inherits the collection's file; `schedule_for` places it at a future slot instead of now. A DIRECT run spends the collection's weekly cadence budget (its posts_per_week) — refused, never queued silently, once the week is spent. ASYNCHRONOUS AND SLOW: 20–60 minutes; returns a run_id immediately, poll get_collection_run (30–60 s apart; polling nudges the run forward).
collection:write
POST
approve_collection_run
Answer the gate: approve a run waiting at 'ready' (or one still producing — the approval is remembered and it publishes at its slot). `publish_now: true` publishes a ready run immediately instead of waiting for the slot.
collection:write
POST
cancel_collection_run
Cancel a run in flight (scheduled, sourcing, writing, ready or publishing). Its slot stays occupied; a queued topic returns to the pool; a claimed spoke goes back on the map.
collection:write
POST
update_auto_pen_config
Set a collection's Auto-Pen configuration — the same settings the app's Auto-Pen panel writes, including which content template its posts compose with. This is a PATCH: omitted fields are left as they are, so changing one setting never blanks the rest. template_key binds a template (from list_templates) as the collection's default, which is how a template authored through this API actually gets used. Returns the full resulting configuration.
collection:write
POST
update_collection
Update a collection in the caller's organization. Provide any of name, description, cadence_target, cadence_period, cover_canvas_type_id, or seo_market; omitted fields are left unchanged. seo_market is the country this collection's keyword demand is measured in (null inherits the org's). cadence_target + cadence_period set the target publishing pace (e.g. 3 per week). cover_canvas_type_id sets which Canvas type mints cover images for the collection's articles (null clears it). See list_canvas_types.
collection:write
POST
configure_collection
Turn on (or reconfigure) a collection's Planner — the recurring content autopilot that sources headlines, drafts them, runs Auto-Pen (Research → Write → Enhance), and publishes on a cadence. The non-phantom mirror of set_phantom_config: one call sets cadence, approval posture, and an optional steering prompt, then materializes the upcoming publish slots. On a collection with no Planner config yet, enabled defaults to true and cadence to 3/week; on an already-configured collection every knob you don't set is left exactly as it was — read the current state with get_collection_config first. Approval defaults to the safe per-collection posture (manual for a client blog — drafts wait for a human); pass approval_mode 'autopilot' only for a blog you're comfortable publishing to with nobody in the loop. The response's `decisions` block says, per knob, whether the value came from you, from the saved config, or from the default. For full/granular control, use update_collection with planner_config.
collection:write
POST
activate_collection
The review is done — turn the collection on. Builds the topic map from the collection's current content areas, seeds a first headline batch from it, then starts the Planner at cadence_per_week/approval_mode with the queue filled — the exact activation the self-serve Guided review step runs on Continue, callable by an integration with its own consent flow. Use after onboard_collection finished (or after adjusting areas with create_cluster/delete_cluster) when you have the user's go-ahead on cadence and approval; omit both to keep the saved values. Returns activated:false with a reason and remedies when the collection has no content areas to build from (set_positioning / create_cluster / re-run onboard_collection) — a precondition, not an error. To reconfigure an already-running Planner without regenerating anything, use configure_collection. Needs a user-scoped key (an acting user for attribution).
collection:write
POST
regenerate_covers
Enqueue a durable cover-regeneration job for a collection and return immediately (covers are minted one at a time, ~45-60s each, so a full sweep can take hours). 'regenerate' (default) re-mints every published post's cover with the collection's current template; 'missing' only mints posts that have no cover yet; 'stock' only sets covers where a stock photo clears the ranker's bar, leaving every other post untouched. At most one job runs per collection at a time: calling this while one is already running returns that job's id instead of starting a second. Poll get_cover_regen_status until status is 'done' (or 'failed').
collection:write
POST
generate_collection_authors
Generate a bank of believable recurring authors for a collection using its name/description and topic map, and add them as active authors. Names only by default; pass with_bios for a one-line bio and masthead role per author. Idempotent by name. Use this to give a phantom site real bylines instead of a fresh throwaway author per post.
collection:write
POST
upsert_collection_author
Create or edit one author in a collection's bank. Provide author_id to edit (omitted fields unchanged) or omit it to create (name required). Fields: name, bio, role, expertise (topic areas), started_at (YYYY-MM-DD, may predate the site), active.
collection:write
POST
delete_collection_author
Remove an author from a collection's bank. Posts already bylined by them keep their byline until reassigned with assign_collection_authors.
collection:write
POST
assign_article_to_collection
Move an article into a collection (or between collections) within the caller's organization. Both the article and the target collection must belong to the caller's org.
collection:write
POST
set_topic
Queue a topic as the collection's next piece to write: the earliest open slot on the schedule, or a new slot extending the calendar if none is open. Pass topic_id (an open spoke from list_topics) to queue from the topic map, or suggestion_id to queue an already-sourced candidate directly — provide exactly one. The Planner must already be set up and turned on for the collection.
collection:write
POST
set_collection_context
Edit a collection's own layer of context — editorial fields only (identity belongs to the offering). Omitted fields are unchanged; a sent list replaces this collection's list. stances, avoid_topics and blocked_mentions are ADDED to what the offering and org already say (this collection's stances lead in prompts; a collection can only be stricter, never remove an inherited avoid topic). priority_topics REPLACES the inherited focus (null = inherit again). default_framing null = inherit. Returns the re-resolved context, as get_collection_context does.
collection:write
POST
set_collection_offerings
Replace a collection's mention set: the offerings (list_offerings) it may promote. Exactly one must be is_primary — the offering the collection publishes as or mostly promotes, and the parent its context inherits from. A main blog has just its primary; a phantom may list others it can mention, and the planner picks one per topic, biased by weight (override a pick with set_topic_offering). Pass an empty list to make the org itself the subject again.
collection:write
POST
set_topic_offering
Override which offering a planner topic's post promotes (the planner picks one per topic from the collection's mention set at plan time). The offering must be in that collection's mention set (get_collection_context → offerings); null clears the override so the post falls back to the collection's primary. Applies to drafts written from the topic after the change.
collection:write
POST
create_automation
Add an automation to a letterbase: a topic (cluster_id of a pillar, or target_id alone for a goal-led topic), a series (cluster_id), a signal (seer_id) or best. Refused when it would say the same thing as one already there (a second automation on the same pillar, series, target or seer; a pillar and the goal-led topic its bound target already writes), or push the weekly total past the cap. The letterbase's schedule follows the new total.
collection:write
POST
update_automation
Change an automation: its pace, on or off, or a pillar topic's goal (the ranking target it aims at). The weekly total stays within the cap; the schedule follows it.
collection:write
POST
delete_automation
Remove an automation from a letterbase. A removed series is paused and a removed seer stops writing here; the schedule follows the new weekly total.
collection:write
POST
refresh_internal_links
Run a collection's next batch of internal-link refreshes now instead of waiting for the nightly sweep — the same 'Refresh links now' the collection room offers (lib/services/relink.ts refreshCollectionLinksNow). Same batch size as a night. Runs even with the policy off (asking is the opt-in). Each started post re-links from the collection's own sitemap and republishes when it finishes; on a manual-approval or client-CMS collection the links are proposed for approval instead (posts_proposed; see list_revisions). Returns as soon as the work is queued.
collection:write
POST
save_enhancer_file
Save a new version of a collection's enhance file and run every pass with it from now on. Send `file` (a whole file; only `name` is required, the rest is completed) or `patch` (only the fields to change, laid over the file the collection runs now, e.g. {"internalLinks":{"maxPerPost":3}}). The first save makes your own copy of our file; later platform releases arrive as an offer (get_enhancer_file `offer`, accept_enhancer_offer). A save that would run exactly like the current file is refused.
collection:write
POST
pin_enhancer_version
Pin a collection to one of our released enhancer versions instead of following our live one (version_id), or unpin it (null). A collection with its own copy runs its copy; the pin applies when it has none.
collection:write
POST
accept_enhancer_offer
Accept our newer platform enhancer version into your collection's copy: a new live version of your file = our live file with your own changes (`offer.kept`) laid back over it. Refused when there is no offer.
collection:write