# Nouns

URL: /code/file-conventions/nouns · Id: `page:code/file-conventions/nouns`

> A file in nouns/ declares one Noun the Startup adds or refines, with its properties, Verbs and states, and compiles to its API.

A file in `nouns/` declares one [Noun](/business/nouns/noun) the [Startup](/business/nouns/startup) adds or refines. `ls nouns/` lists the Startup's own API, and [Nouns](/business/nouns-and-verbs) shows every Noun it inherits.

```text
nouns/
├── Lease.mdx                 the Lease Noun: properties, Verbs, states; the body is its docs
├── Lease.renew.ts            the Function behind Lease.renew
└── Lease.renew.test.ts       its cases
```

## Reference
The file name is the Noun's name, capitalized as it is written in code. Frontmatter is the record, and the body is the Noun's documentation on every [Interface](/business/nouns/interface).

| Key | Type | Holds |
| --- | --- | --- |
| `extends` | Noun names | The Nouns it inherits from, such as `Thing` or `Task`; several are allowed |
| `properties` | map | Each property's type: a scalar, `A \| B` for an enum, or a Noun name for a reference. A name ending in `?` is optional |
| `verbs` | map | The [Verbs](/business/nouns/verb) it declares beyond create, get, list, update and delete, each with its inputs |
| `states` | list | The lifecycle, compiled from the Verbs; write it only where the compiled order is wrong |

### Verb inputs
Each key under `verbs` is a Verb, and its value maps each input to a type. Inputs take the same types as properties, and a name ending in `?` is optional. `{}` declares a Verb that takes only the record.

```yaml
verbs:
  renew: { months: integer, increase?: number }
  notify: {}
```

The record comes first, named for the Noun, so `renew` takes `lease`, `months` and `increase`. Each Verb compiles to one input type in `.sb/types/`, named for the Noun and Verb:

```ts title=".sb/types/Lease.d.ts"
interface LeaseRenewInput {
  lease: SB.Id
  months: number
  increase?: number
}
```

`$.Lease.renew` checks its input against that type, and the [Verb function](/code/file-conventions/verb-functions) takes it as its second argument.

## Examples
### Minimal
```mdx title="nouns/Lease.mdx"
---
properties:
  unit: string
---
```

### Leaseline
Leaseline tracks lease renewals for small property managers. It declares a `Lease` Noun.

```mdx title="nouns/Lease.mdx"
---
extends: [Thing]
properties:
  unit: string
  resident?: Contact
  endsOn: date
  rent?: money
  status: active | renewing | renewed | ended
verbs:
  renew: { months: integer, increase?: number }
  notify: {}
---
A Lease is one resident's agreement on one unit, from signing to renewal.
```

## What it builds
The [SoftwareBuilder](/code/builders/software) compiles the file. One `Lease.mdx` produces:

- the `Lease` Noun with `create`, `get`, `list`, `update` and `delete`
- `Lease.renew`, which takes `lease`, `months` and an optional `increase`, and `Lease.notify`, which takes `lease`, each in every form: `renew`, `renewing`, `renewed`, `renewedBy`
- the [Events](/business/nouns/event) `Lease.created`, `Lease.renewed` and `Lease.notified`
- typed SDK methods, CLI operations, MCP tools and OpenAPI paths, with types in `.sb/types/`
- a screen per record and a form per Verb in every [App](/business/nouns/app) that mounts the Noun

## Routing
| Interface | [Form](/business/nouns/form) |
| --- | --- |
| API | `GET /leaseline.app/leases`, `GET /leaseline.app/leases/{id}`, `POST /leaseline.app/leases/{lease}/renew` |
| SDK | `$.Lease.list()`, `$.Lease.get({ id })`, `$.Lease.renew({ lease, months: 12 })` |
| CLI | `npx startup-builder do Lease.renew --json '{"lease":"lease_1","months":12}'` |
| MCP | `do` with `$.Lease.renew({ lease: 'lease_1', months: 12 })` |

`get`, `update` and `delete` name the record `id`; `renew`, `notify` and every other Verb name it by the Noun, `lease`. The [API spec](/code/api/spec#ids) has the rule.

A Verb is a path segment and never a suffix. A record or [Collection](/business/nouns/collection) named like a declared Verb is refused at write time.

## Good to know
- `status` holds the [states](/business/states). `create` starts it at its first value, and only a Verb or an Event moves it: no `create` or `update` input takes it.
- Nouns sit at the root even when one [Product](/business/nouns/product) uses them, so two Products share one Noun.
- A file named for an inherited Noun refines it: its properties add to the inherited ones.
- The [Function](/business/nouns/function) behind a Verb lives beside the Noun in [Verb functions](/code/file-conventions/verb-functions).