Call the API over plain HTTP
Every tool is a POST with a JSON body. Here's the URL pattern, the auth header, what comes back, and how errors look.
You don't need an agent or the CLI. Every tool is reachable as:
POST https://app.letterstory.com/api/integrations/tools/<tool name>
x-integrations-key: ls_your_key
content-type: application/json
with the tool's arguments as the JSON body. The tool names, arguments, and return shapes are documented at /docs, which also serves an OpenAPI 3.1 document you can load into any client generator. If you're setting up a fresh account from code, the Self-serve setup link on that page (/docs/onboard) walks the whole thing through: import or create content, connect your front end, set the publish webhook, and it includes a prompt you can hand to your own agent.
An example
List your writing kernels:
curl -X POST https://app.letterstory.com/api/integrations/tools/list_kernels \
-H "x-integrations-key: ls_your_key" \
-H "content-type: application/json" \
-d '{}'
{
"items": [
{
"kernel_id": "9f1c2a6e-…",
"name": "Hazel-v0.3",
"description": "Plain, direct, short paragraphs…",
"image_url": "https://…",
"attributed": true,
"created_at": "…",
"updated_at": "…",
"owned": false,
"is_default": true,
"forked_from_name": null
}
]
}
Have a kernel write into a post (endpoint_id comes from list_kernel_endpoints; with no input, it writes from the post's own content):
curl -X POST https://app.letterstory.com/api/integrations/tools/run_kernel \
-H "x-integrations-key: ls_your_key" \
-H "content-type: application/json" \
-d '{"endpoint_id":"3b7e…","article_id":"7c19c7e4-…","kernel_id":"9f1c2a6e-…","input":"Why remote work is here to stay: …"}'
{
"run_id": "…",
"status": "running",
"kernel": { "id": "9f1c2a6e-…", "name": null },
"kernel_job_id": "0a4d8e2b-…",
"article_id": "7c19c7e4-…"
}
Poll it with get_kernel_run_status and the kernel_job_id.
What comes back
A success is the tool's JSON payload, exactly as documented, with status 200. A few curated routes exist for convenience (for example GET /api/integrations/published), but the POST /api/integrations/tools/<name> form works for every tool and is the one to build on.
Errors
| Status | Meaning | What to do |
|---|---|---|
400 | The tool refused the request. The body's error says why: a bad argument, an id not found in your organization, an empty balance or usage allotment, a run already in progress on the post, or a per-tool rate limit. | Read the error and act on it. |
401 | Missing or revoked key. | Check the header; create a new key if it was revoked. |
403 | Key lacks the capability this tool needs. | Create a new key with that capability; existing keys can't be re-scoped. |
404 | No tool by that name. | Check the name against /docs. |
429 | The key's own per-minute limit was hit. Retry-After says how long. | Wait and retry; nothing was started. |
500 | Something broke on our side. | Retry once; then tell support with the request you made. |
Error bodies are always { "error": "…" }. The curated routes (such as GET /api/integrations/published) keep finer statuses: 402 for an empty primitive balance or usage allotment, 403 for a resource your key may not reach, 404 for an id not found, 409 for a run already in progress.
The MCP wire format, if you'd rather speak it
The same server answers JSON-RPC 2.0 at POST /api/mcp:
{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "list_kernels", "arguments": {} } }
The result wraps the payload as a text block: result.content[0].text is the JSON string above. tools/list returns the catalog with input and output schemas. Most people should use the REST form; the MCP form is for MCP clients.
Rate limits
Every key has its own limit in requests per minute (1000 unless set otherwise when the key was made). On top of that, per key:
- Starting AI work (kernel, arena, collection and seer runs, shreds, Enhance passes, phantom plans, image generation): 5 per minute.
- Creating or configuring things (collections, endpoints, templates, connections): 10 per minute.
- Onboarding a domain or collection, sitemap imports, competitor scrapes and site audits: 5 per hour.
Going over the key's own limit returns 429 with Retry-After and X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset; successful responses don't carry these headers. A per-tool limit comes back as an error naming the limit. Details in KB-802.