URL: /code/apps/seo · Id: `page:/code/apps/seo`

# SEO

> The search terms and AI questions your pages target, where each ranks, which answers cite you, and whether every page is indexed.

The search terms and AI questions your pages target, where each ranks, which answers cite you, and whether every page is indexed.

- **System:** [SEO](https://startupbuilder.dev/systems/seo)
- **Nouns:** 4
- **Verbs:** 4
- **Events:** 4

## Nouns
| Noun | What it is |
| --- | --- |
| [Keyword](https://startupbuilder.dev/business/nouns/keyword) | A Keyword is a search phrase or AI prompt that pages target, with its rank and citations observed over time. |
| [Page](https://startupbuilder.dev/business/nouns/page) | A page is an addressable page of a Site, of a named type such as pricing, about one Thing or each member of a Collection. |
| [Monitor](https://startupbuilder.dev/business/nouns/monitor) | A Monitor is a scheduled check on something outside a startup’s control, such as uptime, a Keyword’s rank or an AI answer, recording Events. |
| [Domain](https://startupbuilder.dev/business/nouns/domain) | A Domain is a hostname that points at a Thing, with a role, a status, and a lifecycle tracked as data. |

## Properties
The hub defines each Noun’s core Properties; SEO adds the ones a sale needs.

### Keyword
| Property | Type | What it holds | Defined by |
| --- | --- | --- | --- |
| `phrase` | `string` | The words searched or asked. | The hub |
| `form` | `string` | One of `search` or `question`. | The hub |
| `target` | `Page` | The one page that should rank for it; two pages on one term split it. | SEO |
| `difficulty` | `number` | 0 to 100, from `Keyword.measure`; your ceiling is what your links reach. | SEO |
| `position` | `number \| null` | Where the target ranks, from the latest `Keyword.search`; null is not in the top 100. | SEO |

### Page
| Property | Type | What it holds | Defined by |
| --- | --- | --- | --- |
| `path` | `string` | The path under its Site, such as `/pricing`. | The hub |
| `kind` | `string` | The page type, such as `landing`, `pricing`, or `terms`. | The hub |
| `from` | `string` | Lineage: the slot it compiles from, as `Hypothesis#<slot>@v<version>`. | The hub |
| `indexed` | `boolean` | Whether Google and Bing hold it, read through your Search Console and Bing Webmaster accounts. | SEO |
| `cited` | [`VenueId[]`](/code/data/types#venueid) | The answer engines that cite it, from `Keyword.ask`. | SEO |

### Monitor
| Property | Type | What it holds | Defined by |
| --- | --- | --- | --- |
| `kind` | `string` | `internal` watches the startup’s own routes and jobs; `outside` watches the world, such as a rival’s price. | The hub |
| `target` | `string` | What it checks: a route, a Domain, a Keyword, a prompt or a page. | The hub |
| `every` | `string` | How often it runs, such as `1m` or `1d`. | The hub |
| `lastResult` | `string` | The result of its latest check. | The hub |

### Domain
| Property | Type | What it holds | Defined by |
| --- | --- | --- | --- |
| `hostname` | `string` | The name, such as `example.com`. | The hub |
| `role` | `string` | What the Domain does for the Thing it serves; a defensive name takes `redirect`. | The hub |
| `acquired` | `string` | One of `claimed`, `purchased`, or `connected`. | The hub |
| `state` | `string` | Where the Domain stands; `verifying`, `live`, `degraded`, `expiring` and `lapsed` are observed, the rest follow an Action. | The hub |
| `registrant` | `party` | The owner of record: the holding Company while held, or the holder of a self-serve startup’s Stripe account. | The hub |
| `expiresAt` | `datetime` | When the registration ends, as the registrar reports it; empty for a claimed Domain. | The hub |
| `autoRenew` | `boolean` | True when renewal runs without a Decision; false sends the Domain through renewal review. | The hub |
| `renewalPrice` | `money` | What the next renewal costs, which for a premium name differs from the purchase price. | The hub |
| `transferableAt` | `datetime` | The first moment the registrar allows a transfer out, after any registration, transfer or registrant lock. | The hub |
| `names` | `string` | How its subdomains are given out: explicit records only, or one-label claims by other startups. | The hub |
| `publicSuffix` | `boolean` | True once the public suffix list browsers ship carries the Domain; an issuing Domain gives out no label before then. | The hub |
| `registrarAccount` | `string` | The registrar account that holds the Domain: one per startup, never shared with a sibling. | The hub |
| `registryLock` | `boolean` | True when the registry refuses transfer, update and delete until a verified out-of-band request lifts it. | The hub |
| `certificate` | `json` | Certificate state (`pending`, `active`, `renewing` or `failed`), its expiry, and the failing check with its fix. | The hub |
| `validation` | `string` | `delegated` proves control through a CNAME to the platform; `serving` is for an apex that cannot hold one. | The hub |
| `walletsEnabled` | `boolean` | True once Stripe lists the Domain as a payment method domain on the startup’s account. | The hub |
| `carriedIdentity` | `boolean` | True once the Domain received mail, hosted a Mailbox or verified an outside account; it never resets. | The hub |
| `to` | `domain` | For a redirect: the canonical Domain it forwards to permanently, path and query kept. | The hub |

## Verbs
| Verb | What it does | Approval |
| --- | --- | --- |
| [`Keyword.track`](/capabilities/measure/search-readout) | Reads, each week, where your pages rank for the terms you target and which AI answers name you. | Runs on its own |
| [`Keyword.create`](https://startupbuilder.dev/business/nouns/keyword/create) | Saves a term you target, with its locale and the page that should rank for it. | Runs on its own |
| [`Keyword.update`](https://startupbuilder.dev/business/nouns/keyword/update) | Changes a saved term’s target page or form, search or question. | Runs on its own |
| [`Monitor.update`](https://startupbuilder.dev/business/nouns/monitor/update) | Pauses a Monitor, or changes how often it runs or what counts as a change. | Runs on its own |

## Reads the market
A Data Verb reads the market and returns a reading. A Software Verb changes your records and emits an Event. SEO saves what these Data Verbs return; they are not its own Verbs.

[`Keyword.find`](/code/data/keywords/find) · [`Keyword.measure`](/code/data/keywords/measure) · [`Keyword.search`](/code/data/keywords/search) · [`Keyword.ask`](/code/data/keywords/ask) · [`Domain.benchmark`](/code/data/domains/benchmark) · [`Domain.verify`](/code/data/domains/verify) · [`Monitor.create`](/code/data/monitors/create) · [`Monitor.check`](/code/data/monitors/check)

## Events
| Event | Emitted by |
| --- | --- |
| `Keyword.tracked` | [`Keyword.track`](/capabilities/measure/search-readout) |
| `Keyword.created` | [`Keyword.create`](https://startupbuilder.dev/business/nouns/keyword/create) |
| `Keyword.updated` | [`Keyword.update`](https://startupbuilder.dev/business/nouns/keyword/update) |
| `Monitor.updated` | [`Monitor.update`](https://startupbuilder.dev/business/nouns/monitor/update) |

A [Trigger](https://startupbuilder.dev/business/nouns/trigger) can start a Workflow on any of them, and a Watch delivers each to your endpoint, signed ([Events and webhooks](/code/events)).

## Who operates it
| Part | SEO |
| --- | --- |
| Accountable | [Content lead](https://startupbuilder.dev/agents/marketing/content-lead) |
| Also calls its Verbs | [Marketing lead](https://startupbuilder.dev/agents/marketing/marketing-lead) |
| Seat | An agent holds each seat until you seat a person in it. |
| Asks you first | None of its Verbs: each runs within its seat’s Limits |

## Integrations
[Google Search Console](/code/integrations/google-search-console) · [Bing Webmaster](/code/integrations/bing-webmaster) · [Google Analytics](/code/integrations/google-analytics)

## Import and export
| Direction | What | How |
| --- | --- | --- |
| Import | Your Search Console and Bing Webmaster history | `Integration.connect`; queries, clicks and index state come in as Events |
| Export | Ranks and citations over time | `Metric.query` over `Keyword.searched` and `Keyword.asked` |

## Example
**A term picked, targeted and tracked.**

1. `Keyword.create` saves “rent ledger template” with `/rent-ledger-template` as its target.
2. The page goes live, and `Keyword.track` reads its position and the AI answers each week.
3. By week 6 it sits at 18: indexed and in range, so the page is rewritten from the query’s wording.