URL: /capabilities/make-content/answer-pages · Id: `page:/capabilities/make-content/answer-pages`

# Answer page per question

> A buyer’s question in; out comes a page on your site that settles it in 60 words, every claim sourced, then found by search engines.

A buyer’s question in; out comes a page on your site that settles it in 60 words, every claim sourced, then found by search engines.

```text title="Run it · Ask"
Get Leaseline named in AI answers for the 10 questions small landlords ask most about tracking rent, spend up to $15, and show me the plan first.
```

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

const result = await $.try.Page.answer({ goal: 'Get Leaseline named in AI answers for the 10 questions small landlords ask most about tracking rent, spend up to $15, and show me the plan first.', budget: { amount: 15, currency: 'USD' } })
```

**Plan.** (1) Find candidate questions: `Keyword.find` in question form, 200 rows, about 10¢. (2) Rank them in the buyer’s own words: `Keyword.search` for problem posts on Reddit, Hacker News and X, 12 searches, about 1¢. (3) Measure volume and intent: `Keyword.measure`, 200 terms, about 10¢. (4) Read who is named now, and whether any page of yours answers: AI answer audit, 10 questions × 4 engines, about 25¢. (5) Write a page for each unanswered question: Answer page per question, about 8 pages at about 12¢, about 96¢. (6) Get each page indexed: Index the site, free on your own accounts; it waits days. (7) Read the answers again at day 21: AI answer audit, about 15¢. (8) The Key Result: 10 of 10 questions answered by an indexed page of yours by 2026-11-17. Answer share is reported at day 21, not promised. Estimate $1.65 of $15.00.

**Receipt.** 11 calls, $1.94 spent, 10 of 10 questions answered by an indexed page.

- **Kind:** Agent task
- **Price:** 12¢ per page
- **Your time:** 6 minutes
- **Approval:** Goes out on its own
- **Deepens:** [`workflow:publish-content`](https://startupbuilder.dev/workflows/processes/content-marketing/publish-content)

## What you get
- **1 page** · A buyer’s question in; out comes a page on your site that settles it in 60 words, every claim sourced, then found by search engines. · [`noun:page`](https://startupbuilder.dev/business/nouns/page)
- `page` · The published page: URL, title, word count and status.
- `claims[]` · Every claim on the page with the record or cited page it rests on.
- `review` · Both Review passes: the adversarial verdict with what it cut, and the affirmative one with what it added.
- `indexed` · `pending` when it returns; Index the site reads it daily until it is indexed or names why not.

**Connect once:** [Google Search Console](/code/integrations/google-search-console). **How long:** About 8 minutes; then it waits days on the index.

## Trigger
It runs when you call it, per question, or in a batch from the AI answer audit’s unanswered questions.

```ts
on.Keyword.audited((audit, $) => audit.questions.filter((q) => !q.yourPage?.answers).map((q) => $.Page.answer({ question: q.question, site: 'leaseline.app' })))
```

## 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 results page for the question | [`Keyword.search`](/code/data/keywords/search) | The top 10 results, and the forum or video slots in them | About 0.03¢ | Runs on its own |
| 2. Ask four answer engines the question | [`Keyword.ask`](/code/data/keywords/ask) | Who each engine names and the pages it cites | About 1.6¢ | Runs on its own |
| 3. Read the five pages answers cite most | [`Page.audit`](/code/data/pages-and-sites/audit-page) | What the cited pages say, and what they leave out | About 0.1¢ | Runs on its own |
| 4. Choose the angle and the outline | `Content.draft` | What the cited pages miss that your records can show, as an outline with a source per section | About 1¢ | Runs on its own |
| 5. Write the page | `Content.draft` | The page: a 60-word answer first, then the detail, an FAQ block and its schema | About 9¢ | Runs on its own |
| 6. Audit the page before it goes live | [`Page.audit`](/code/data/pages-and-sites/audit-page) | Title, headings, schema and links checked | About 0.02¢ | Runs on its own |
| 7. Publish it on your site | `Content.publish` | The page live, which pings the engines as an effect of the publish | Free | Goes out on its own |
| 8. Read its index state | [`Domain.verify`](/code/data/domains/verify) | The page’s index state, handed to Index the site | Free | Runs on its own |

Calls: `Keyword.search`, then `Keyword.ask`, then `Page.audit`, then `Content.draft`, then `Page.audit`, then `Content.publish`, then `Domain.verify`.

About 12¢ a page, each time it runs, capped at 30¢.

## Example: give it a goal
> Get Leaseline named in AI answers for the 10 questions small landlords ask most about tracking rent, spend up to $15, and show me the plan first.

Run it above shows the plan it showed before it spent anything, and the receipt of every call.

## When something fails

| When | Then |
| --- | --- |
| The adversarial Review fails | The page is redrafted once with the findings; a second failure leaves a draft, raises a task and settles at cost with nothing published. |
| An existing page nearly answers the question | With `update` set, or in goal mode as a Decision, the page is revised as a new version: an update beats a near-duplicate. |
| The page is crawled and not indexed after 7 days | Index the site presses Request indexing on it when `pressRequest` is on, about 16¢; otherwise it is reported with the engine’s reason. |
| You call it again for the same question and site | It updates the same page as a new version: it is idempotent per question and site. |

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-page-answer-1-<step>-<item>`.

## How it is graded
A second model checks every claim on the page against its cited sources and your offer; Index the site reads whether each engine holds it. It passes when the page passes the adversarial review, goes live on your site, and Index the site reads it indexed or names the engine’s reason. A miss is redone at no charge.

## Change it
| Ask | Compiles to |
| --- | --- |
| Update my features page instead of writing a new one. | [`Page.answer`](/capabilities/make-content/answer-pages) |
| Write one for every question my audit found unanswered. | [`Page.answer`](/capabilities/make-content/answer-pages) |
| Write eight of these every month from my keyword map. | [`Site.grow`](/capabilities/make-content/monthly-search-content) |

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

export default workflow({
  name: 'answer-page',
  verb: 'Page.answer',
  input: { question: 'string', site: 'string', facts: "'offer' | string[]", update: 'string?' },
  maxCost: 0.3,
  review: [
    { pattern: 'adversarial', rubric: 'every claim resolves to a record or a cited page' },
    { pattern: 'affirmative', rubric: 'answers in the first 60 words; FAQ block; schema' },
  ],
  grade: { evaluator: 'claims against the cited pages and the Offer; index state read back', passWhen: 'live, reviewed, and indexed or a reason', remedy: 'redo' },
  execute: async (ctx, { question, site, facts = 'offer', update }) => {
    const results = await ctx.step('results', () => $.Keyword.search({ keywords: [question], venue: 'google-search', maxCost: 0.01 }))
    const answers = await ctx.step('answers', () => $.Keyword.ask({ questions: [question], venues: ['chatgpt', 'perplexity', 'gemini', 'google-ai-mode'], maxCost: 0.03 }))
    const cited = await ctx.step('cited', () => $.Page.audit({ urls: topCited(answers, 5), fields: ['content'], maxCost: 0.01 }))
    const draft = await ctx.step('write', () => $.Content.draft({ kind: 'answer-page', question, from: { results, answers, cited }, facts, update, maxCost: 0.08 }))
    const page = await ctx.step('publish', () => $.Content.publish({ content: draft.$id, site }))   // reviewed before it publishes
    return { page, indexed: (await ctx.step('index', () => $.Domain.verify({ hostname: site, pages: [page.url] }))).pages[0].state }
  },
})
```

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

## Works with
**Plays that order it:** [Answer engine optimization](/plays/aeo), [Category hubs](/plays/category-hubs), [Switch kit](/plays/switch-kit).

It serves [Make content](/capabilities/make-content), and runs in [Launch and search](/strategy/launch-and-search). It deepens [`workflow:publish-content`](https://startupbuilder.dev/workflows/processes/content-marketing/publish-content).

## For agents

| Fact | Value |
| --- | --- |
| Guide | `Page.answer` |
| Guide approval | `limits` |
| Goal | Get Leaseline named in AI answers for the 10 questions small landlords ask most about tracking rent, spend up to $15, 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 | `on.Keyword.audited((audit, $) => audit.questions.filter((q) => !q.yourPage?.answers).map((q) => $.Page.answer({ question: q.question, site: 'leaseline.app' })))` |
| Retry | Three times with backoff, then the next route; the same idempotency key never charges, sends or posts twice |
| Idempotency key | `leaseline-page-answer-1` |
| Lookups | A `find:` input asks unless exactly one record fits |

| Step | Action class | Seat rule that would hold it |
| --- | --- | --- |
| 1. Read the results page for the question | None | None. |
| 2. Ask four answer engines the question | None | None. |
| 3. Read the five pages answers cite most | None | None. |
| 4. Choose the angle and the outline | None | None. |
| 5. Write the page | None | None. |
| 6. Audit the page before it goes live | None | None. |
| 7. Publish it on your site | `publish` | None. |
| 8. Read its index state | None | None. |

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

| Step | Function | Doer | Checker | Assurance | Inputs |
| --- | --- | --- | --- | --- | --- |
| 1. Read the results page for the question | `code` | Code | Code checks each result against its declared type | `measured` | `$0.question` |
| 2. Ask four answer engines the question | `code` | Code | Code checks each result against its declared type | `measured` | `$0.question` |
| 3. Read the five pages answers cite most | `code` | Code | Code checks each result against its declared type | `measured` | `$1, $2` |
| 4. Choose the angle and the outline | `agentic` | The [Content lead](/capabilities/agents/content-lead) agent | A second model grades the output | `graded` | `$3, $0.facts` |
| 5. Write the page | `agentic` | The [Content lead](/capabilities/agents/content-lead) agent | Affirmative review by a second model: Pass, with what it added: the 60-word answer, the FAQ, the schema | `graded` | `$4.outline, $0.facts` |
| 6. Audit the page before it goes live | `code` | Code | Code checks each result against its declared type | `measured` | `$5.draft` |
| 7. Publish it on your site | `code` | Code | Code reads the result back from where it landed | `measured` | `$6.draft, $0.site` |
| 8. Read its index state | `code` | Code | Code checks each result against its declared type | `measured` | `$7.url` |

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) |
| Nouns | [`noun:keyword`](https://startupbuilder.dev/business/nouns/keyword), [`noun:page`](https://startupbuilder.dev/business/nouns/page), [`noun:content`](https://startupbuilder.dev/business/nouns/content), [`noun:action`](https://startupbuilder.dev/business/nouns/action), [`noun:domain`](https://startupbuilder.dev/business/nouns/domain) |
| Events | `Content.drafted`, `Action.evaluated`, `Content.published`, `Domain.verified` |

## Reference

| Name | Type | Default | Meaning |
| --- | --- | --- | --- |
| `question` | `string` | Required | The question, in the buyer’s words, as the AI answer audit or a problem post found it. |
| `site` | `Hostname` | Required | Your site. |
| `facts` | `'offer' \| string[]` | `'offer'` | The records a claim may rest on: your offer, your pages, or a list. A claim with no source is cut, never softened. |
| `update` | `Url` | Required | An existing page that nearly answers the question: it is revised as a new version instead of adding a near-duplicate. |
| `maxCost` | `number` | `0.30` | 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 `Page.answered`. Refuses with [`max-cost`](/code/refusals#max-cost) before it spends, and with the Data calls and Accounts groups ([refusals](/code/refusals)).