Foundation setup
Set up your organization’s context
Manifesto, positioning, identity, ranking targets and competitors: what your topic map, GEO matching and writing all read. Do this before your main blog (/docs/onboard/collection). Follow the steps or hand the prompt to your agent.
Before you start
- 1Create an API key (Settings → API keys → Create key). Send it in the x-integrations-key header, not Authorization: Bearer.
- 2Gather what you have: notes, an about page, a deck or press links. Nothing? Step 2 below covers it.
You are setting up a Letterstory organization's context: what it IS, what it should write about, what it should rank/be cited for, and who it competes with. This feeds every downstream system — the topic map, GEO mention-matching, and on-brand generation all read this — so treat it as foundational setup, not optional polish. Work through the phases in order; a from-scratch org should not skip to collection/blog setup at /docs/onboard/collection before this is done.
Connect first:
- This guide is for an EXISTING Letterstory customer. If the user has somehow not signed up yet, that happens at https://app.letterstory.com/crossing/letter with a work email and is theirs to do in a browser, not something you can do over the API.
- Ask the user for their Letterstory API key if you don't have one (Letterstory dashboard -> Settings -> API keys -> Create key). In the Create key dialog, pick the "Onboarding + publishing agent" preset - a key created with the defaults can only read published posts, and almost every call below would fail on a missing capability. Keys are not issued over the API, so this step also happens in the browser. Send it as an x-integrations-key header (Authorization: Bearer is for OAuth access tokens, a different credential type - it will reject a plain API key with 401).
- Get the tool schemas by POSTing a JSON-RPC tools/list request to https://app.letterstory.com/api/mcp - plain HTTP is fine, you do not need an MCP client. That is the ONLY place per-tool parameters live. https://app.letterstory.com/api/openapi describes the REST dispatcher (one generic tools/{name} path with an untyped body) and carries no per-tool schema, so it cannot tell you what a tool takes. Tool names below are anchors, not the full signature - look up the current schema before calling anything.
- Call get_onboarding_plan first. Its context step is this guide: done means positioning is already set (then this is a refinement, not first-time setup), ready means start here, blocked on domain_set means call set_company_info with the public site domain before anything else. The same plan shows what the Planner and phantom steps are waiting on, which is usually this step.
- Call get_organization to see the current domain/name/description. If the org has real material to draw from (a live site, notes, docs), go to Phase 1. If it's a genuine blank slate with nothing to feed in, skip to Phase 2.
- THE ENGINE MAY HAVE DONE THIS ALREADY. onboard_collection reads the org's public site during its first stage and writes positioning, identity, voice and visuals from it - a measured 27 fields on a real run. So if the plan's collection step is done, positioning is the engine's inference from the site, not a blank slate: call get_positioning FIRST and treat this guide as correcting what it got wrong, not as filling in an empty org. Re-ingesting the same site to "set up" an org that onboarding already read wastes a call and can overwrite a human's correction with the same machine guess.
Phase 1 - Feed what you know (the normal path)
- Ask the user for anything they have: free-text notes about the company, links (about page, pitch deck, product docs, press), and whether to include a fresh read of their own website.
- Call ingest_context with whatever combination of text / links / include_website you gathered. This does NOT persist anything - it returns a reviewable proposal: a list of changes across positioning, competitors, identity, voice/branding and visuals, each with a stable id, a before/after, and a rationale.
- Walk the user through the proposal, especially identity and positioning changes since those are the most consequential (identity in particular decides what counts as "this company" being cited elsewhere - get it wrong and GEO mention-matching gets it wrong everywhere downstream). Ask which change ids to accept; don't auto-accept everything without at least surfacing what's about to change.
- Call apply_context with the ingestion_id and the accepted change ids. Only accepted changes are written - positioning is versioned, competitors are only ever added (never silently removed), identity and visuals are merged rather than replaced.
- Verify what you can, and be honest about what you cannot. get_positioning reads back priority_topics/stances/avoid_topics; list_competitors reads back competitors; the MANIFESTO lands on get_organization's description field, NOT on get_positioning, which has no such field. Identity (what the company is, what it is not, its descriptors, its one-liner and its default framing) has no read-back tool at all right now - get_brand_context returns null unless import_brand_context populated it, and its own description says the judgement half is not there. So do not claim identity was applied because apply_context returned ok; say plainly that it reported success and cannot currently be read back.
- Important: there is no standalone "set identity" tool. is / is-not claims, descriptors, the one-liner, and default framing - the org's Identity - can ONLY be set or corrected through ingest_context -> apply_context. If the user wants to fix just one identity fact later, run ingest_context again with a short note describing the correction; don't go looking for a more direct tool that doesn't exist.
Phase 2 - No existing material? Bootstrap from just a domain
- If there was nothing to feed into Phase 1 (no notes, no links, no meaningful existing site), call suggest_onboarding instead (optionally passing domain, else it uses the org's stored one). It drafts an org description, editorial positioning (priority_topics / stances / avoid_topics), and evidence-based ranking targets in a single read-only call - nothing is persisted by this call itself.
- Apply the draft explicitly and tell the user you're doing so: set_company_info (manifesto: the drafted description), set_positioning (the drafted priority_topics/stances/avoid_topics), and add_ranking_target once per suggested target you and the user want to keep.
- Verify: get_positioning and list_ranking_targets show what you actually applied, not just what suggest_onboarding drafted.
Phase 3 - Ranking targets (if not already fully covered by Phase 1 or 2)
- suggest_ranking_targets any time for more evidence-based candidates - it combines positioning stances, what the client's own site already ranks for, and what AI assistants already name them for.
- add_ranking_target for each one worth tracking. There's an active-target cap - if you hit it, the error message names the current limit; retire a weaker target with update_ranking_target before adding past it rather than treating the cap as a hard stop.
- Verify: list_ranking_targets.
Phase 4 - Competitors (if not already fully covered by Phase 1 or 2)
- guess_geo_competitors for AI-suggested competitor domains. It returns configured: false with an empty list when no competitor-search provider is set up for this deployment - tell the user that plainly rather than reporting "no competitors found."
- add_competitor for each one to track. A company can't be added as its own competitor (the call is rejected) - if that happens, don't retry with a slightly different spelling of the same domain, it's a real guard. The domain is scraped in the background on first add so its topics feed positioning context.
- Verify: list_competitors.
Guardrails:
- This is customer-facing self-serve context setup. It is not the staff-run "phantom"/hosted-storefront tooling - don't reach for plan_phantom, set_phantom_config, onboard_domain, create_phantom, etc. here.
- Re-running ingest_context later is the correct, intended way to correct what the system believes about this org - it's not a one-time setup step you graduate out of.
- Once this is in reasonable shape, move to https://app.letterstory.com/docs/onboard/collection to create or configure the main blog collection - it reads positioning to build its topic map, so sequencing matters.
- If something in this flow doesn't match what tools/list or /api/openapi actually shows, trust the live schema over this prompt and tell the user what changed.The four pieces
Feed what you know
Give the agent notes, links or your website. ingest_context returns a proposal (nothing saved) for positioning, competitors, identity, voice and visuals, each with a before/after and a reason. Check identity closely: it decides which mentions count as you. apply_context saves only what you accept.
- ingest_context
- apply_context
- get_positioning
- list_competitors
- get_brand_context
Nothing to feed in? Bootstrap from a domain
Starting from nothing? suggest_onboarding drafts a description, positioning and ranking targets from your domain. It saves nothing; apply what you want with set_company_info, set_positioning and add_ranking_target.
- suggest_onboarding
- set_company_info
- set_positioning
- add_ranking_target
Ranking goals
suggest_ranking_targets drafts candidates from your positioning, rankings and AI mentions. add_ranking_target tracks one. Active targets are capped; retire weak ones with update_ranking_target.
- suggest_ranking_targets
- add_ranking_target
- update_ranking_target
- list_ranking_targets
Competitors
guess_geo_competitors suggests domains from your positioning (configured: false means no search provider is set up). add_competitor tracks one and scrapes it in the background. You can't add your own domain.
- guess_geo_competitors
- add_competitor
- list_competitors