URL: /capabilities/publish-and-post/founder-posts · Id: `page:/capabilities/publish-and-post/founder-posts`

# Founder posts

> Each week, three posts in your voice for LinkedIn and X, written from what you shipped and learned, every number from a record.

Each week, three posts in your voice for LinkedIn and X, written from what you shipped and learned, every number from a record.

```text title="Run it · Ask"
Write three LinkedIn posts this week from what I shipped, spend up to 50¢, and show me the plan first.
```

```ts title="Run it · SDK"
import { $ } from 'startup-builder'

const result = await $.try.Post.write({ goal: 'Write three LinkedIn posts this week from what I shipped, spend up to 50¢, and show me the plan first.', budget: { amount: 0.5, currency: 'USD' } })
```

```json title="Run it · Response"
{
  "mode": "try",
  "stored": false,
  "estimate": "About 6.2¢ per 3 posts",
  "sample": {
    "account": "LinkedIn",
    "at": "Mon 08:30",
    "topic": "the $100 message test",
    "source": "experiments/exp_9",
    "status": "scheduled"
  }
}
```

- **Kind:** Agent task
- **Price:** 6.2¢ per 3 posts
- **Your time:** 12 minutes
- **Approval:** Runs on its own
- **Deepens:** [`process:content-marketing`](https://startupbuilder.dev/workflows/processes/content-marketing)

## What you get
- **3 posts** · Each week, three posts in your voice for LinkedIn and X, written from what you shipped and learned, every number from a record. · [`noun:post`](https://startupbuilder.dev/business/nouns/post)
- `posts[]` · Each post: the account, when it goes up, the text, and the record it came from.
- `held[]` · posts that failed the grade twice, with the finding.
- `skipped` · Topics left out, such as a change with no user-facing effect.

**Connect once:** [LinkedIn](/code/integrations/linkedin), [X account](/code/integrations/x). **How long:** Minutes; posts go up through the week.

## Trigger
It runs weekly at 08:00 on the first weekday.

```ts
every('mon 08:00', ($) => $.Post.write({ handles: ['linkedin:founder', 'x:founder'], perWeek: 3 }))
```

## Steps
Your agent does these steps, one row per step; each step’s check is part of its row and its price.

| Step | Verb | Returns | Price | Approval |
| --- | --- | --- | --- | --- |
| 1. Read the week’s material | `Collection.query` | Call notes, shipped changes, answers that worked and the latest test readout | Free | Runs on its own |
| 2. Choose three topics | `Content.draft` | Three topics a buyer would stop for, each tied to one record | About 0.2¢ | Runs on its own |
| 3. Write each LinkedIn post | `Content.draft` | A post per topic, in your voice, with the record’s numbers | About 3¢ | Runs on its own |
| 4. Cut each into an X version | `Content.draft` | The same point in X’s length, with the link in a reply | About 3¢ | Runs on its own |
| 5. Schedule them on your accounts | [`Post.schedule`](/code/apps/social/schedule) | Each post scheduled at the account’s best slot | Free | Runs on its own |

Calls: `Collection.query`, then `Content.draft`, then `Post.schedule`.

About 6.2¢ per 3 posts, each time it runs, capped at 25¢.

## Example: give it a goal
> Write three LinkedIn posts this week from what I shipped, spend up to 50¢, and show me the plan first.

The plan it shows runs 5 steps, priced before it spends anything; Run it above shows the result it returns.

## When something fails

| When | Then |
| --- | --- |
| The week has no new material | Nothing is scheduled and the readout says so; it never invents a topic. |
| A post fails the grade | It is rewritten once; a second failure holds it with the finding, and it is not billed. |
| An account needs signing in again | Scheduling stops on that account with the fix line; the others go ahead. |
| You call it twice in one week | The week’s posts are returned: it is idempotent per handle, ISO week and slot. |

Each step retries three times, then falls through to the next route; one item's failure lands in `skipped` with its reason, never failing the call. Before a step would cross `maxCost` it stops and returns what it has, with a `resume` link. Each step's idempotency key is the call's key plus the step and the item: `leaseline-post-write-1-<step>-<item>`.

## How it is graded
A second model reads each post beside your last 20 posts and the record it cites. It passes when it reads as you on the voice samples, every number matches its record, and it makes one point. A miss is redone at no charge.

## Change it
| Ask | Compiles to |
| --- | --- |
| Only LinkedIn, and only from what I shipped. | [`Post.write`](/capabilities/publish-and-post/founder-posts) |
| Harvest the people who comment, two days after each post. | [`Audience.harvest`](/capabilities/find-customers/post-engagers) |
| Turn the best post into a page on my site. | [`Page.answer`](/capabilities/make-content/answer-pages) |

```ts
import { $, workflow } from 'startup-builder'

export default workflow({
  name: 'post-write',
  verb: 'Post.write',
  input: { handles: 'string[]', perWeek: 'integer', from: "('calls' | 'changes' | 'answers' | 'experiments')[]" },
  trigger: { schedule: 'mon 08:00' },
  maxCost: 0.25,
  grade: { evaluator: 'your last 20 posts; the cited record', passWhen: 'reads as you; every number matches; one point', remedy: 'redo' },
  execute: async (ctx, { handles = ['linkedin:founder', 'x:founder'], perWeek = 3, from = ['calls', 'changes', 'answers', 'experiments'] }) => {
    const week = await ctx.step('source', () => $.Collection.query({ from, since: '7d' }))
    if (!week.rows.length) return { posts: [], skipped: 'nothing new this week' }
    const posts = await ctx.step('write', () => $.Content.draft({ kind: 'founder-posts', from: week.rows, count: perWeek, handles, maxCost: 0.1 }))
    return ctx.step('schedule', () => $.Post.schedule({ posts: posts.map((p) => p.$id), slots: 'best' }))   // graded before it schedules
  },
})
```

Extend it with `extends: 'Post.write'` and `base()`, or [compose your own](/code/compose) from these steps.

## Works with
**Plays that order it:** [Reuse what you already published](/plays/archive-repurposing), [Build in public](/plays/build-in-public), [Category kit](/plays/category-kit), [Founder voice](/plays/founder-voice), [Organic social](/plays/organic-social), [Owned newsletter](/plays/owned-newsletter).

It serves [Publish and post](/capabilities/publish-and-post). It deepens [`process:content-marketing`](https://startupbuilder.dev/workflows/processes/content-marketing).

## For agents

| Fact | Value |
| --- | --- |
| Guide | `Post.write` |
| Guide approval | `limits` |
| Goal | Write three LinkedIn posts this week from what I shipped, spend up to 50¢, and show me the plan first. It may change counts, the order of steps, soft filters and the route a read takes, and it stops early when the goal is met. It never relaxes your words, raises its own budget, answers its own approval, calls a Play or acts outside your standing approval. |
| Trigger | `every('mon 08:00', ($) => $.Post.write({ handles: ['linkedin:founder', 'x:founder'], perWeek: 3 }))` |
| Retry | Three times with backoff, then the next route; the same idempotency key never charges, sends or posts twice |
| Idempotency key | `leaseline-post-write-1` |
| Lookups | A `find:` input asks unless exactly one record fits |

| Step | Action class | Seat rule that would hold it |
| --- | --- | --- |
| 1. Read the week’s material | None | None. |
| 2. Choose three topics | None | None. |
| 3. Write each LinkedIn post | None | None. |
| 4. Cut each into an X version | None | None. |
| 5. Schedule them on your accounts | None | None. |

The amounts live once, on the hub ([approvals](https://startupbuilder.dev/agents/approvals)).

| Step | Function | Doer | Checker | Assurance | Inputs |
| --- | --- | --- | --- | --- | --- |
| 1. Read the week’s material | `code` | Code | Code checks each result against its declared type | `measured` | `$0.from` |
| 2. Choose three topics | `agentic` | The [Content lead](/capabilities/agents/content-lead) agent | A second model grades the output | `graded` | `$1` |
| 3. Write each LinkedIn post | `agentic` | The [Content lead](/capabilities/agents/content-lead) agent | A second model grades the output | `graded` | `$2.topics, $0.voice` |
| 4. Cut each into an X version | `agentic` | The [Content lead](/capabilities/agents/content-lead) agent | Adversarial review by a second model: Pass, or the claim cut | `graded` | `$3.posts` |
| 5. Schedule them on your accounts | `code` | Code | Code reads the result back from where it landed | `measured` | `$4.passed, $0.handles` |

The doer is never the checker, and the weakest assurance on the path is the whole call’s.

## Under the hood

| Kind | What it touches |
| --- | --- |
| Systems | [Sites and content](/code/apps/sites-and-content), [Social](/code/apps/social) |
| Nouns | [`noun:collection`](https://startupbuilder.dev/business/nouns/collection), [`noun:content`](https://startupbuilder.dev/business/nouns/content), [`noun:action`](https://startupbuilder.dev/business/nouns/action), [`noun:post`](https://startupbuilder.dev/business/nouns/post) |
| Events | `Collection.queried`, `Content.drafted`, `Action.evaluated`, `Post.scheduled` |

## Reference

| Name | Type | Default | Meaning |
| --- | --- | --- | --- |
| `handles` | `string[]` | `['linkedin:founder', 'x:founder']` | The accounts it posts as. |
| `perWeek` | `integer` | `3` | Posts a week, at most. Fewer when the week had less to say. |
| `from` | `('calls' \| 'changes' \| 'answers' \| 'experiments')[]` | `['calls', 'changes', 'answers', 'experiments']` | Where topics come from: call notes, shipped changes, answers that worked, test readouts. |
| `voice` | `File` | `your last 20 posts` | Samples of how you write; the voice grade reads against them. |
| `maxCost` | `number` | `0.25` | The most this call spends each time it runs, in USD. It stops before a step would cross it and returns what it has, with a `resume` link. |

Emits `Post.written`. Refuses with [`max-cost`](/code/refusals#max-cost) before it spends, and with the Data calls and Accounts groups ([refusals](/code/refusals)).