# Domain

URL: /business/nouns/domain · Id: `noun:domain`

> A Domain is a hostname that points at a Thing, with a role, a status, and a lifecycle tracked as data.

A Domain is a hostname that points at a [Thing](/business/nouns/thing), with a role, a status, and a lifecycle tracked as data.

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

## Object
```json
{
  "$id": "https://api.sb/kilnbakery.com/domains/domain_1",
  "$type": "Domain",
  "hostname": "kilnbakery.com",
  "role": "canonical",
  "acquired": "claimed",
  "state": "verifying",
  "registrant": "party_1",
  "expiresAt": "2026-10-22T12:00:00Z",
  "autoRenew": true,
  "renewalPrice": {
    "amount": 9,
    "currency": "USD"
  },
  "transferableAt": "2026-10-22T12:00:00Z",
  "names": "closed",
  "publicSuffix": true,
  "registrarAccount": "registrar_account_1",
  "registryLock": true,
  "certificate": {},
  "validation": "delegated",
  "walletsEnabled": true,
  "carriedIdentity": true,
  "to": "domain_1"
}
```

## Relationships
- <a id="rel-hosts"></a>A Domain **hosts** one [Site](/business/nouns/site): a [Site](/business/nouns/site) answers on one or more Domains; each Domain serves one Site. `hosts`, inverse [`hostedAt`](/business/nouns/site#rel-hosted-at).
- <a id="rel-mounts"></a>A Domain **mounts** many [Interfaces](/business/nouns/interface): an [Interface](/business/nouns/interface) answers requests on the hostnames it is mounted on. `mounts`, inverse [`mountedOn`](/business/nouns/interface#rel-mounted-on).
- <a id="rel-records"></a>A Domain **records** many [DNS Records](/business/nouns/dns-record): a Domain answers DNS from its records; each record belongs to one Domain. `records`, inverse [`answersFor`](/business/nouns/dns-record#rel-answers-for).
- <a id="rel-registered-to"></a>A Domain **is registered to** one [Startup](/business/nouns/startup): a [Startup](/business/nouns/startup) holds each Domain registration and pays for the hostname. `registeredTo`, inverse [`domains`](/business/nouns/startup#rel-domains).
- <a id="rel-serves"></a>A Domain **serves** one [Tenant](/business/nouns/tenant): a [Tenant](/business/nouns/tenant) answers on the names its [Customer](/business/nouns/customer) connects; each such Domain serves one Tenant. `serves`, inverse [`hostnames`](/business/nouns/tenant#rel-hostnames).
- <a id="rel-verified-by"></a>A Domain **is verified by** one [Tenant](/business/nouns/tenant): a [Tenant](/business/nouns/tenant) proves control of its [Customer](/business/nouns/customer)’s email domain, so sign-ins on that domain follow the Tenant’s policy. `verifiedBy`, inverse [`verifies`](/business/nouns/tenant#rel-verifies).

## Lifecycle

| State | Meaning | Moves to |
| --- | --- | --- |
| `candidate` (first) | A name found by search that no one here holds yet. | `held` by [Domain.register](/business/nouns/domain/register); `registered` by [Domain.register](/business/nouns/domain/register); `claimed` by [Domain.claim](/business/nouns/domain/claim); `connected` by [Domain.connect](/business/nouns/domain/connect) |
| `held` | Registered to the holding Company and not yet assigned to a Startup. | `claimed` by [Domain.claim](/business/nouns/domain/claim); `released` by [Domain.release](/business/nouns/domain/release) |
| `claimed` | Assigned to a Startup: a label under an issuing Domain, or a held Domain. | `live` by [Domain.verify](/business/nouns/domain/verify); `released` by [Domain.release](/business/nouns/domain/release) |
| `registered` | Registered to its owner of record and pointed at the Startup’s address. | `verifying` by [Domain.verify](/business/nouns/domain/verify) |
| `connected` | Attached from another registrar while its ownership proof is checked. | `verifying` by [Domain.verify](/business/nouns/domain/verify) |
| `verifying` | Its first DNS, ownership and certificate checks are running. | `live` by [Domain.verify](/business/nouns/domain/verify); `degraded` by [Domain.verify](/business/nouns/domain/verify) |
| `live` | Serving, with its DNS, ownership and certificate checks passing. | `degraded` by [Domain.verify](/business/nouns/domain/verify); `expiring` on [`Domain.renewalDue`](/business/nouns/domain#domainrenewaldue); `renewed` by [Domain.renew](/business/nouns/domain/renew); `transferred` by [Domain.transfer](/business/nouns/domain/transfer); `released` by [Domain.release](/business/nouns/domain/release) |
| `degraded` | A check failed; a degraded canonical Domain opens an Incident. | `live` by [Domain.verify](/business/nouns/domain/verify); `released` by [Domain.release](/business/nouns/domain/release) |
| `expiring` | Inside its renewal window with no renewal yet. | `renewed` by [Domain.renew](/business/nouns/domain/renew); `lapsed` on [`Domain.expired`](/business/nouns/domain#domainexpired) |
| `lapsed` | Its registration ended unrenewed, and the registrar may still restore it. | `renewed` by [Domain.renew](/business/nouns/domain/renew); `released` by [Domain.release](/business/nouns/domain/release) |
| `renewed` | Extended by whole years at its renewal price. | `live` by [Domain.verify](/business/nouns/domain/verify) |
| `transferred` | Moved to another holder or registrar, with its record and history kept. | None: it ends here. |
| `released` | Its registration or claim ended, and its record, history and aliases are kept. | None: it ends here. |

## Verbs
| Verb | Description | Subject | Event | Class |
| --- | --- | --- | --- | --- |
| [Domain.authenticate](/business/nouns/domain/authenticate) | Authenticate writes a Domain’s SPF, DKIM and DMARC records and verifies them before its first send. | [Security Engineer](/agents/engineering/security-engineer) | [`Domain.authenticated`](/business/events/Domain.authenticated) | Two-way: `try`, `send`, `do` |
| [Domain.benchmark](/business/nouns/domain/benchmark) | Benchmark measures a Domain’s estimated traffic, ranking Keywords, authority and referring domains beside the competitors named. | [Analyst](/agents/marketing/analyst) | [`Domain.benchmarked`](/business/events/Domain.benchmarked) | Two-way: `try`, `send`, `do` |
| [Domain.claim](/business/nouns/domain/claim) | Claim gives a Startup a one-label name under an issuing Domain, or assigns it a held Domain through an approved Change. | [Engineering Lead](/agents/engineering/engineering-lead) | [`Domain.claimed`](/business/events/Domain.claimed) | Two-way: `try`, `send`, `do` |
| [Domain.connect](/business/nouns/domain/connect) | Connect attaches a hostname a Startup already holds and verifies ownership through DNS. | [Integration Engineer](/agents/engineering/integration-engineer) | [`Domain.connected`](/business/events/Domain.connected) | Two-way: `try`, `send`, `do` |
| [Domain.register](/business/nouns/domain/register) | Register buys a hostname for a Startup and points it at the Startup’s address. | [Integration Engineer](/agents/engineering/integration-engineer) | [`Domain.registered`](/business/events/Domain.registered) | One-way (money): `send`, `do` |
| [Domain.release](/business/nouns/domain/release) | Release lets a Domain’s registration or claim end, keeping its record, history and aliases. | [Legal Lead](/agents/legal/legal-lead) | [`Domain.released`](/business/events/Domain.released) | One-way (customer, brand): `send`, `do` |
| [Domain.renew](/business/nouns/domain/renew) | Renew extends a Domain’s registration by whole years at its renewal price, paid by its owner of record. | [Treasurer](/agents/finance/treasurer) | [`Domain.renewed`](/business/events/Domain.renewed) | One-way (money): `send`, `do` |
| [Domain.search](/business/nouns/domain/search) | Search lists candidate names with availability, first-year price, renewal price and the rules of each extension. | [Engineering Lead](/agents/engineering/engineering-lead) | [`Domain.searched`](/business/events/Domain.searched) | Two-way: `try`, `send`, `do` |
| [Domain.transfer](/business/nouns/domain/transfer) | Transfer moves a Domain’s registration to another holder or registrar, with its records intact. | [Legal Lead](/agents/legal/legal-lead) | [`Domain.transferred`](/business/events/Domain.transferred) | One-way (customer): `send`, `do` |
| [Domain.verify](/business/nouns/domain/verify) | Verify checks a Domain’s DNS, ownership and certificate again and records each result, with the fix for any failure. | [Reliability Engineer](/agents/engineering/reliability-engineer) | [`Domain.verified`](/business/events/Domain.verified) | Two-way: `try`, `send`, `do` |
| <a id="create"></a>[Domain.create](/business/nouns/domain/create) | Creates a Domain. Inherited from [Noun](/business/nouns/noun). | None. | `Domain.created` | Two-way: `try`, `send`, `do` |
| <a id="get"></a>[Domain.get](/business/nouns/domain/get) | Returns one Domain by id. Inherited from [Noun](/business/nouns/noun). | None. | None. | Read: `try`, `do` |
| <a id="list"></a>[Domain.list](/business/nouns/domain/list) | Returns Domains, newest first, one page at a time. Inherited from [Noun](/business/nouns/noun). | None. | None. | Read: `try`, `do` |
| <a id="update"></a>[Domain.update](/business/nouns/domain/update) | Changes the given properties of a Domain. Inherited from [Noun](/business/nouns/noun). | None. | `Domain.updated` | Two-way: `try`, `send`, `do` |
| <a id="delete"></a>[Domain.delete](/business/nouns/domain/delete) | Deletes a Domain; its history is kept. Inherited from [Noun](/business/nouns/noun). | None. | `Domain.deleted` | One-way: `send`, `do` |

## Events
Events no Verb emits. Each Verb’s Event is in the Verbs table above; every Event is delivered in the same shape.

### Domain.expired
A Domain’s registration passed `expiresAt` unrenewed; the registrar may still restore it. Raised by a clock.

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

```json
{
  "$id": "https://api.sb/kilnbakery.com/events/event_1",
  "$type": "Event",
  "name": "Domain.expired",
  "at": "2026-10-22T12:00:00Z",
  "data": {
    "domain": {
      "$id": "https://api.sb/kilnbakery.com/domains/domain_1",
      "$type": "Domain",
      "hostname": "kilnbakery.com",
      "role": "canonical",
      "acquired": "claimed",
      "state": "lapsed",
      "registrant": "party_1",
      "expiresAt": "2026-10-22T12:00:00Z",
      "autoRenew": true,
      "renewalPrice": {
        "amount": 9,
        "currency": "USD"
      },
      "transferableAt": "2026-10-22T12:00:00Z",
      "names": "closed",
      "publicSuffix": true,
      "registrarAccount": "registrar_account_1",
      "registryLock": true,
      "certificate": {},
      "validation": "delegated",
      "walletsEnabled": true,
      "carriedIdentity": true,
      "to": "domain_1"
    },
    "previous": {
      "state": "expiring"
    }
  },
  "seq": 1
}
```

### Domain.renewalDue
A live Domain entered its renewal window with no renewal yet. Raised by a clock.

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

```json
{
  "$id": "https://api.sb/kilnbakery.com/events/event_1",
  "$type": "Event",
  "name": "Domain.renewalDue",
  "at": "2026-10-22T12:00:00Z",
  "data": {
    "domain": {
      "$id": "https://api.sb/kilnbakery.com/domains/domain_1",
      "$type": "Domain",
      "hostname": "kilnbakery.com",
      "role": "canonical",
      "acquired": "claimed",
      "state": "expiring",
      "registrant": "party_1",
      "expiresAt": "2026-10-22T12:00:00Z",
      "autoRenew": true,
      "renewalPrice": {
        "amount": 9,
        "currency": "USD"
      },
      "transferableAt": "2026-10-22T12:00:00Z",
      "names": "closed",
      "publicSuffix": true,
      "registrarAccount": "registrar_account_1",
      "registryLock": true,
      "certificate": {},
      "validation": "delegated",
      "walletsEnabled": true,
      "carriedIdentity": true,
      "to": "domain_1"
    },
    "daysLeft": 30,
    "previous": {
      "state": "live"
    }
  },
  "seq": 1
}
```

## Permissions
Which Access level may call each Verb. One-way Verbs wait their delay window.

| Operation | `owner` | `admin` | `member` | `viewer` |
| --- | --- | --- | --- | --- |
| `Domain.create` | Yes | Yes | Yes | No |
| `Domain.get` | Yes | Yes | Yes | Yes |
| `Domain.list` | Yes | Yes | Yes | Yes |
| `Domain.update` | Yes | Yes | Yes | No |
| `Domain.delete` | Yes | Yes | Yes | No |
| `Domain.authenticate` | Yes | Yes | Yes | No |
| `Domain.benchmark` | Yes | Yes | Yes | No |
| `Domain.claim` | Yes | Yes | Yes | No |
| `Domain.connect` | Yes | Yes | Yes | No |
| `Domain.register` | Yes | No | No | No |
| `Domain.release` | Yes | No | No | No |
| `Domain.renew` | Yes | No | No | No |
| `Domain.search` | Yes | Yes | Yes | No |
| `Domain.transfer` | Yes | No | No | No |
| `Domain.verify` | Yes | Yes | Yes | No |

## Call it

**Ask**

```text
Register the domain “patentpad.com” with a startup.
```

**API**

```bash
curl -X POST https://api.sb/patentpad.com/domains/register \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"hostname":"patentpad.com","startup":"startup_1"}'
```

```json
{
  "api": "https://api.sb/patentpad.com",
  "domain": {
    "$id": "https://api.sb/patentpad.com/domains/domain_1",
    "$type": "Domain",
    "hostname": "patentpad.com",
    "role": "canonical",
    "state": "verifying"
  },
  "action": {
    "$id": "https://api.sb/patentpad.com/actions/action_1",
    "operation": "Domain.register",
    "status": "done"
  },
  "links": {
    "self": "https://api.sb/patentpad.com/actions/action_1"
  }
}
```

**SDK**

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

const domain = await $.Domain.register({ hostname: 'patentpad.com', startup: 'startup_1' })
```

**CLI**

```bash
npx startup-builder do Domain.register --json '{"hostname":"patentpad.com","startup":"startup_1"}'
```

**MCP**

```json
{
  "name": "do",
  "arguments": {
    "code": "return await $.Domain.register({ hostname: 'patentpad.com', startup: 'startup_1' })"
  }
}
```

## Example
```text
hostname            role        state   to
permitly.dev        canonical   live            the Site, /docs, /portal, /api and /mcp
api.permitly.dev    alias       live            the same API as /api
permitly.com        redirect    live    permitly.dev
```