IndieBob is agentic
Not as a label — as a fact you can check. Every capability on this page runs with an API key alone: no browser session, no human click. An agent can read what a project earned, draft and publish a post, pull the prospects, enrol them in a sequence, read the emails Bob wrote for each one, approve a first touch, and ask what to do next.
The document
One capability document, two forms — both generated from the code that serves the endpoints, so neither can describe something IndieBob does not do.
MCP
The same capabilities as MCP tools. One local stdio process, nothing to host — your key stays in your own client config and never reaches a broker.
MCP client config
{
"mcpServers": {
"indiebob": {
"command": "npx",
"args": [
"-y",
"@indiebob/mcp"
],
"env": {
"INDIEBOB_API_KEY": "ib_sk_…"
}
}
}
} Claude Code: claude mcp add indiebob --env INDIEBOB_API_KEY=ib_sk_… -- npx -y @indiebob/mcp
Claude Desktop
Download indiebob.mcpb and open it. Claude Desktop installs the same server as a Desktop Extension, asks for your IndieBob API key, and stores it in your system keychain — no config file to edit.
Published on npm as @indiebob/mcp and in the official MCP registry as io.github.chemic/indiebob.
What this looks like across a whole project — facts sheet, published post, attribution, prospects, the approved first touch and Do Next — is walked end to end in How it all links together.
Keys
Agent access uses an account-level service key (ib_sk_) with an explicit scope list and an expiry. Keys are minted by the account owner — email [email protected] and say what you want the agent to do. The raw key is shown once and stored only as a hash.
A key for everything on this page
read:projects · read:metrics · read:actions · read:content · write:content · read:outreach · write:outreach
Grant less and the rest still works. Ask for a scope you were not given and the answer is a 403 that names the missing scope — so your agent can tell you exactly what to add instead of failing vaguely.
Capabilities
| Tool | Endpoint | Scope |
|---|---|---|
| whoamiConfirm the key is live and list the scopes it holds. Returns no portfolio data, so it is safe to call first. | GET /api/v1 | read:projects |
| list_projectsEvery project the key owner has, with health status, stage and settings. Start here: most other capabilities take a project id or slug. | GET /api/v1/projects | read:projects |
| read_metricsDaily snapshots per project: MRR, visitors, signups, revenue, health. Date-windowed, not cursor-based, because snapshots are restated when late Stripe events land — re-pull a trailing window and upsert. | GET /api/v1/metrics | read:metrics |
| next_actionsThe ranked Do Next feed across the whole portfolio — the same rules the Command Center runs, highest priority first, each with why it fired and where it leads. Deterministic: no model runs, so the answer is the same for the same data. | GET /api/v1/actions | read:actions |
| list_postsBlog posts in the key owner's projects, newest-updated first, with status and SEO score. Bodies are not included — use get_post. | GET /api/v1/content/posts | read:content |
| get_postOne post with its markdown body and SEO fields. A post outside the key owner's portfolio 404s exactly like a nonexistent one. | GET /api/v1/content/posts/{postId} | read:content |
| create_postCreate a draft. Markdown is the source of truth — HTML cannot be submitted, it is rendered server-side. The slug is derived from the title. | POST /api/v1/content/posts | write:content |
| publish_postPublish or re-publish. THE propagation trigger: it snapshots the body, assigns the OG image and fires the content webhook that pushes the post to mirrored sites. Nothing else takes a post live. | POST /api/v1/content/posts/{postId}/publish | write:content |
| list_prospectsCold-outreach prospects for one project, newest first, with status, source and the facts enrichment read off their own website. | GET /api/v1/outreach/prospects | read:outreach |
| list_sequencesThe project's sequences with their status, step count, whether each is personalised (Bob writes every email per prospect) and how many prospects are enrolled. | GET /api/v1/outreach/sequences | read:outreach |
| enroll_prospectsEnrol up to 200 prospects. Unsubscribed, rejected and already-enrolled prospects are skipped and counted back, never silently dropped. On a personalised sequence this opens each first touch for Bob to write; if the plan has fewer emails left than prospects, the whole call is refused with 429 rather than parking them behind a limit. | POST /api/v1/outreach/sequences/{sequenceId}/enroll | write:outreach |
| list_review_queueThe personalised first touches waiting for a human yes — each with the subject and opening Bob wrote for that prospect, and the prospect it is addressed to. This is what a human approves in the dashboard; an agent can read the same rows. | GET /api/v1/outreach/messages | read:outreach |
| approve_messageThe yes on one personalised first touch. Sending window, daily caps and the sequence's sending account still apply afterwards — approving does not send. A message that is not awaiting review answers 409 saying what state it is in. | POST /api/v1/outreach/messages/{messageId}/approve | write:outreach |
Full parameters, response shapes and a working example per capability: /agents.md.
What an agent cannot do
Billing, key minting, project creation and settings stay in the browser: an agent can run the growth loop, not change what your account is or what it pays. Approving a first touch does not send it — the sequence's send window, its daily cap and its sending account still decide when the email leaves. And nothing here writes to another account's projects: every id is resolved inside the key owner's portfolio, so someone else's project is a 404.
Publishing blog posts to your own site instead? That is the Content API, which uses a per-project ib_pk_ key.