Back to blog

How it all links together

·7 min read

Most marketing tools are a drawer of separate things. A writer here, a scheduler there, an analytics tab that has never heard of either. You do the linking, in your head, on a Sunday.

IndieBob is one loop, and the links are the product. This is that loop walked end to end on one project — every screen real, every command below run once before it was written down. At the end of each step is the same sentence in a different costume: this step is a tool an agent can call.

1. The facts sheet: one true description of your product

Nothing downstream is any good if the machine writing it is guessing about your product. So the loop starts with a facts sheet: what the thing is, who it's for, what it costs, the claims you are willing to stand behind.

The Connect step where a project's facts sheet is filled in

Every draft, every cold email, every social post is written from this sheet. It's why a post about your app reads like your app, and why Bob doesn't invent a feature you never shipped — the sheet is the only place claims come from.

Connect verifies five steps and reports what it actually observed, not what you ticked: SDK events, facts, revenue, publishing, first post.

2. A post, drafted and published — with its card

Write in the editor or ask Bob for a draft, then publish. The post hub is the artifact's own page: status, its live URL once traffic proves it, the SEO audit with an Apply button, and the distribution row.

The post hub — status, live URL, performance, audit and distribution for one post

The share card is composed, not generated: your real logo, your real type, your real capture at the venue's size. We don't let a model paint a picture of your app — an invented screenshot is an invented claim, and it ships to everyone who sees the link.

Over the API the same publish is two calls:

curl -X POST https://www.indiebob.com/api/v1/content/posts \
  -H "Authorization: Bearer ib_sk_…" -H 'Content-Type: application/json' \
  -d '{"project":"my-app","title":"How it all links together","bodyMarkdown":"…"}'

curl -X POST https://www.indiebob.com/api/v1/content/posts/<id>/publish \
  -H "Authorization: Bearer ib_sk_…"

That is exactly how this post reached this page: markdown in the repo, pushed through POST /api/v1/content/posts as a draft, read, then published. No special path for us.

3. The SDK: first touch, kept until it pays

Drop @indiebob/tracker in your app and the loop closes. It's under 5 KB gzipped, sets no cookies, and its first job is to remember where somebody came from — the UTM, the referrer, the post they landed on — and keep it through signup and through Stripe.

import { IndieBob } from '@indiebob/tracker'
const bob = IndieBob.init({ projectId: 'proj_…' })
bob.identify('user_123', { email: '[email protected]' })

The SDK install guide inside a project's Connect path

That's what makes "this post earned €X" a fact rather than a vibe. The Earned page reads it back per post and per source, and where there is no data it says so with an em dash instead of a confident zero.

4. A prospect, found where indie devs actually launch

The other half of the loop is the people who haven't heard of you. Discovery reads the venues where products get launched and maintained and pulls out businesses that match your facts sheet, with the evidence of where each one came from.

Then Bob reads that prospect's own website and writes the subject, an opening about their business, and one bridge sentence into your email. Your claims, your CTA, your sign-off stay yours. The reading and the writing both happen on our own solar-powered, battery-backed AI farm — a prospect's website text never leaves our hardware.

5. The email waits for a human

The first email to each prospect is held until you read it. Follow-ups approve themselves.

The review queue — first touches waiting for a human yes

If the rig is offline or the plan cap is hit, the touch waits with the reason shown in full. It never falls back to the generic template — sending the bland version is exactly the thing the feature exists to prevent. And what went out is stored as it went out: subject, HTML, text, message id, per recipient, forever.

6. Do Next: the loop's answer

The Command Center ranks what to do across every project, from the same data every step above wrote.

The Command Center's Do Next list

Not a feed. A short ordered list where each row is a button into the tool that does it.

And now: your agent can run all of it

Here is the part that changed this week. Every step above is a capability with a name, and the names are not marketing copy — they come from one registry file in the codebase that is rendered five ways, so it cannot drift from the API it describes:

Rendering Where
Markdown for a context window https://www.indiebob.com/agents.md
JSON manifest https://www.indiebob.com/.well-known/agents.json
The human docs page https://www.indiebob.com/docs/agents
The API's own front door GET /api/v1
MCP tool list @indiebob/mcp

Thirteen tools, and you can read them off the published package yourself without an account:

echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
  | INDIEBOB_API_KEY=ib_sk_x npx -y @indiebob/mcp
whoami          list_projects     read_metrics      next_actions
list_posts      get_post          create_post       publish_post
list_prospects  list_sequences    enroll_prospects  list_review_queue
approve_message

Read them against the loop and the map is one to one: list_projects and read_metrics for the numbers, create_post and publish_post for step 2, list_prospects and enroll_prospects for step 4, list_review_queue and approve_message for step 5, next_actions for step 6.

Three ways to wire it up. In Claude Code:

claude mcp add indiebob --env INDIEBOB_API_KEY=ib_sk_… -- npx -y @indiebob/mcp

In Claude Desktop, open indiebob.mcpb — it asks for your key and stores it in your system keychain. In any other MCP client, the same npx -y @indiebob/mcp command in an mcpServers block. The server is in the official MCP registry as io.github.chemic/indiebob.

It runs on your machine, not ours: a hosted endpoint would have to hold or broker every user's key, and one leak would be everybody's prospect list. The package has zero runtime dependencies and unpacks to about 22 KB.

What an agent deliberately cannot do

A capability list is only trustworthy if it has edges. These are ours, and they are a feature:

  • Billing. No agent changes your plan or your card.
  • Key minting. An owner mints keys, in a browser, with their own login.
  • Project creation and settings. Including the facts sheet — the claims a machine writes from are set by a person.
  • Sending. approve_message approves a first touch; it does not send it. The sequence's send window, daily cap and sending account still decide when it leaves.

Scopes are granular and honest about refusal: a key without write:outreach gets a 403 that names the scope it lacked, and a bad key gets a 401. Every id resolves inside the key owner's portfolio, so another account's project is a 404 that looks exactly like a project that never existed.

The bottom of the stack

One last link, because people ask what this runs on. The AI writing your emails and your drafts runs on our own solar-powered, battery-backed farm — that's why per-prospect personalisation is included rather than metered into oblivion. Everything else — the app, the API an agent calls, the queue, and the Postgres holding your events, posts, prospects and transactions — runs on DigitalOcean App Platform and DigitalOcean's managed Postgres. It has been a good place to put an indie product: push to deploy, a database with backups we don't administer, and a bill that stays legible while the thing grows.

Start at /docs/agents, mint a key, and point your agent at the loop.

Written with IndieBob

Share this article

Stay up to date

Get the latest insights on indie software growth, marketing tactics, and building profitable projects.

No spam. Unsubscribe anytime.