/
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
20 tools
Ledes
Turn a prospect's context into scored cold-email opening lines from your editable library.
All 20 tools
POST
list_lede_endpoints
The caller's lede endpoints, each with its callable public id, its standing defaults (the per-call knobs it applies when a call sets none) and lifetime run count.
lede:read
POST
get_lede_endpoint_config
The endpoint's whole configuration as ONE document: its name and its standing run defaults (max candidates, tone, enrichment, disabled ledes, diversity, high temperature; every knob run_lede accepts per call besides the person and recent_ledes, which is per-call only and never a standing default). This is the contract: what you get back is exactly what set_lede_endpoint_config accepts, so read it, edit it, replace it.
lede:read
POST
list_lede_runs
Recent lede calls (telemetry) for the caller's org, newest first. Optionally filter to one endpoint. Each row keeps who the lede was for, what enrichment concluded about the company, and the best line as it was served.
lede:read
POST
lede_line_funnel
Per library line, how it performed for your org: picked, sent, replied, positive, flagged wrong. The counts past `picked` come from your own report_lede_outcome calls, so they measure what you have told the engine, not what the engine guessed.
lede:read
POST
lede_eval_briefing
The state of the lede quality exercise: every eval a reviewer's flag started, how many lines currently trip it, and the lines themselves. An eval here MEASURES and never blocks — it counts how often a category of false positive occurs, so the number falling to zero means the engine was genuinely repaired rather than the symptom suppressed. Read `open` to know what is still broken and where the repair belongs (invariant = engine logic, pattern = a gate or a list, taste = calibration), and `regressions` to catch a fix that has come undone. `proposed` lists evals drafted from a flag — including your own `flagged_wrong` reports — that are baselined but not yet live, with how many runs reported each. Pass `sweep: true` to re-measure against the current corpus first, which is what you want after changing the engine.
lede:read
GET
lede_account_history
What you have ALREADY said to an account: the opening lines this org has sent, the template ids behind them and the observations they used. Call it before running a lede for someone at a company you have contacted before, and skip the template ids it returns so the same person's colleague does not get the same opener. Omit `domain` for a picture across every account.
lede:read
POST
get_lede_balance
The org's lede balance: granted, used, available. Only matched runs you asked the door for directly count as used; a no_match or error run costs nothing, and a run another product triggered on your behalf is recorded but not billed here. Zero available blocks new direct runs.
lede:read
POST
create_lede_endpoint
Create a lede endpoint for the caller's organization. Returns its callable public id; calls authenticate with the org's API key (POST /api/lede/ingest/<public_id>). Set its standing defaults afterwards with set_lede_endpoint_config.
lede:write
POST
set_lede_endpoint_config
Replace the endpoint's whole configuration with one document, the same shape get_lede_endpoint_config returns. Fields left out of `defaults` are cleared (this REPLACES). A per-call field on run_lede still wins over a default.
lede:write
POST
delete_lede_endpoint
Delete one of the caller's lede endpoints. Its past telemetry is preserved.
lede:write
POST
run_lede
Run one prospect through one of the caller's lede endpoints: give it the person's context (name, title, company, work email, city, notes; at least one identifying field), it enriches the company behind them (what it does, the shorthand name of its space, its site's typography and logo), and it answers with opening lines chosen from a curated library, ranked by confidence with the top choice flagged. Library candidates carry stable ids so a caller can avoid repeating a line to one account: track library ids, not text. With high_temperature on and no library line qualifying, the run instead WRITES one strictly checked line for the prospect; such candidates carry generated: true and a per-response 'hot-*' id that is not worth tracking. The endpoint's standing defaults (get_lede_endpoint_config) apply to any knob you leave out; pass enrich: false when latency matters more than fit. Returns { status, best, candidates, enrichment, run_id }; status 'no_match' means nothing shipped for this person. Costs one lede from the org's balance (get_lede_balance) when it matches (library or generated); a no_match run costs nothing. 402-equivalent at zero.
lede:write
POST
report_lede_outcome
Tell the engine what happened to a lede it gave you: that you sent it, that the prospect replied, or that its observation was factually wrong. This is the ONLY way the engine learns whether a line works — it can measure everything up to the moment it hands the line over and nothing after, so an unreported send is invisible. Call it once at send time and again when a reply lands. Idempotent: re-reporting `sent` keeps the first send time. A `flagged_wrong: true` report also drafts a lede eval from that run — a PROPOSED predicate that counts how often the same template and reading recur, baselined over your history and shown under `proposed` in lede_eval_briefing. It never blocks a line and cannot go live on its own: Letterstory staff promote it. Further flags on the same pattern raise its reporter count instead of drafting another.
lede:write
POST
list_ledes
The org's opening-line library, in display order: the pre-built lines (source 'prebuilt', seeded from the code library on first read) and any lines the org authored (source 'custom'), enabled or not. Each row carries its wording (text, plus alts rotated across prospects), its trigger (requires), its 0 to 100 strictness dial (threshold; null or 50 = the engine's calibrated default, higher = pickier), tone, tags, and for a pre-built line the proprietary engine behind its trigger. Runs pick only from enabled lines; the row ids here are what the other lede library tools address.
lede:library:read
POST
list_lede_proposals
Opener proposals: lines a reviewer wrote while personalizing an email on the Dispatch desk, templatized and judged for reuse, each with a drafted 'Use when ...' condition. Default status proposed (waiting for a decision), most often captured first. rejected shows why each was turned down (decided_by null = automatically). Decide with decide_lede_proposal.
lede:library:read
POST
create_lede
Add a custom opening line to the org's library, created enabled and immediately eligible for runs. An optional condition (a Use-when clause) is asked about each prospect in the run's batched Jev call, and the line is used only when the prospect clearly meets it. text (plus optional alts and peer_alts) is the wording; a {{placeholder}} may only print a field the line's requires lists, and every trigger must be a known requirement. Custom lines compose the deterministic and judged triggers freely but may not claim a proprietary-engine trigger; those stay reserved for the pre-built lines, so a hand-authored line can never assert an engine read that never ran. threshold (0 to 100) nudges the trigger's gate strictness around the engine default: 50 = the default, higher = pickier, lower = looser.
lede:library:write
POST
update_lede
Edit one library line: wording (text, alts, peer_alts), trigger (requires), strictness (threshold, 0 to 100 around the engine default of 50; null resets to the default), tone, or tags. Only the fields you send change. The merged result is validated with the line's own source, so a pre-built line keeps its engine triggers through a rewording while a custom line stays limited to the non-engine triggers, and no edit can leave a line printing a placeholder it does not require. To apply an accepted rewrite_ledes_voice proposal, send its text, alts and peer_alts here.
lede:library:write
POST
set_lede_enabled
Enable or disable one library line. A disabled line is never a run candidate but keeps its wording, trigger and history, and can be re-enabled any time. Prefer this over delete_lede for turning a line off: it is reversible, and it is the only way to keep a pre-built line out permanently (a deleted pre-built is re-seeded on the next library read).
lede:library:write
POST
delete_lede
Delete one library line. A deleted custom line is gone; a deleted pre-built line is re-seeded at its seed wording (edits lost) the next time the library is listed, so to keep a pre-built line out of runs use set_lede_enabled instead.
lede:library:write
POST
rewrite_ledes_voice
Propose brand-voice rewrites for the org's library: give it a voice description and it drafts new wordings (text, alts, peer_alts) for every line, or only the rows named in ids. A rewrite changes the wording only; each line's meaning, {{placeholders}}, trigger, threshold and engine stay untouched, and every draft passes the same house-rules checks a hand-written line does. Drafts that fail come back under rejected with their reasons while the original stands. THIS TOOL APPLIES NOTHING: the proposals are for a human to vouch for; apply an accepted proposal with update_lede.
lede:library:write
POST
decide_lede_proposal
Accept or reject an opener proposal (status proposed). accept adds the line to the org's library as a custom line, enabled, with its condition; it is immediately eligible for runs, and a line with a condition is used only for prospects the run's Jev check judges meet it (no answer = the line sits out); pass text, condition or tone to edit them first (the line is validated like any custom line), or condition: null to drop the condition. reject requires a reason, which is read back so the same line is never proposed again. Recorded as decided by the person who owns this API key.
lede:library:write