> ## Documentation Index
> Fetch the complete documentation index at: https://vetta.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Web domains

> The dual-track domain system: independent email and app verification, three registrar kinds, and the readiness gates email sits on.

<Info>**Available now.** Domains ship with Identity, gated by the [policy](/docs/concepts/policies) layer — domain purchase is approval-gated by default.</Info>

A **domain** is the foundation an identity's [email](/docs/identity/email) sits on, and the anchor for serving apps over HTTP. Vetta models a domain with **two independent verification tracks** that must not be conflated:

<CardGroup cols={2}>
  <Card title="Email track" icon="mail">
    Governs whether the domain can send and receive mail. Exposed as `status` (overall) plus `dns_status` (mail DNS).
  </Card>

  <Card title="App / HTTP track" icon="globe">
    Governs whether the domain can serve an app over HTTP. Exposed as `app_connect_status`, entirely separate from the email track.
  </Card>
</CardGroup>

<Warning>
  A domain can be fully verified for email (`status: active`, `dns_status: provisioned`) while its app track is still `pending` — and vice-versa. Always check the specific track for the capability you need.
</Warning>

## Verification tracks and their states

| Capability                    | Required track state                                         |
| ----------------------------- | ------------------------------------------------------------ |
| Create an inbox on the domain | `status: active`                                             |
| **Send** mail                 | `dns_status: provisioned`                                    |
| **Receive** mail              | domain verified for inbound at the email layer (MX in place) |
| Serve an app over HTTP        | `app_connect_status: connected`                              |

## System domains: auto-provisioning at signup

Every organization gets a working `system` domain automatically. Provisioning is **fire-and-forget** — it runs in the background at signup and self-heals, so a usable domain is ready without any action from you.

<Steps>
  <Step title="Create at the email layer">
    Vetta creates the domain at the email layer with sending **and** receiving enabled.
  </Step>

  <Step title="Write DNS into the shared apex zone">
    The required SPF/DKIM/MX/DMARC records are written directly into Vetta's shared apex zone — no manual step, because Vetta controls the zone.
  </Step>

  <Step title="Trigger verification and poll briefly">
    Verification is triggered immediately and polled for a short window. When it clears, the domain row is usable.
  </Step>

  <Step title="Background healing">
    A background sweep periodically re-verifies any domain still `pending`, so transient DNS propagation delays resolve themselves.
  </Step>
</Steps>

```bash CLI theme={"system"}
vetta identity domain list
# NAME                          KIND     STATUS   DNS_STATUS    APP_CONNECT
# acme.<vetta-apex>             system   active   provisioned   pending
```

<Note>
  Because system domains are list-only, the resulting `{org-slug}.<vetta-apex>` subdomain is the fastest way to get an agent sending and receiving mail — no registrar steps at all.
</Note>

## BYOD: connect and verify your own domain

Connecting an `external` domain is a two-call flow. `connect` returns the DNS records to add **at your registrar**; `verify` checks them and advances both tracks.

<Steps>
  <Step title="Connect the domain">
    Vetta returns a set of DNS records to add at your registrar. The exact set is returned **dynamically** — typically a `TXT` for SPF, `TXT`/`CNAME` for DKIM, `MX` for inbound, sometimes a `CNAME`, plus a `_dmarc` `TXT`.

    ```bash CLI theme={"system"}
    vetta identity domain connect --domain acme-mail.com
    ```

    ```jsonc theme={"system"}
    // records to add at YOUR registrar (illustrative — the real set is returned to you)
    [
      { "type": "TXT",   "host": "@",              "value": "v=spf1 include:... ~all" },
      { "type": "CNAME", "host": "s1._domainkey",  "value": "s1.dkim...." },
      { "type": "MX",    "host": "@",              "value": "10 inbound....", "priority": 10 },
      { "type": "TXT",   "host": "_dmarc",         "value": "v=DMARC1; p=none; ..." }
    ]
    ```
  </Step>

  <Step title="Add the records at your registrar">
    Add every returned record exactly. Propagation can take a few minutes to a few hours depending on your registrar.
  </Step>

  <Step title="Verify">
    ```bash CLI theme={"system"}
    vetta identity domain verify --domain acme-mail.com
    ```

    Verification advances the two tracks independently:
    <br />• Company/email track: `pending_dns` → `active`
    <br />• DNS track: `pending_verification` → `provisioned`
  </Step>
</Steps>

<CodeGroup>
  ```typescript TypeScript theme={"system"}
  const domain = await vetta.domains.create({
    name: "acme-mail.com",
    kind: "external",
  });
  // domain.records -> the DNS records to add at your registrar

  await vetta.domains.verify(domain.id);
  ```

  ```bash cURL theme={"system"}
  curl https://api.vetta.sh/v1/domains \
    -H "Authorization: Bearer $VETTA_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "name": "acme-mail.com", "kind": "external" }'

  curl https://api.vetta.sh/v1/domains/$DOMAIN_ID/verify \
    -H "Authorization: Bearer $VETTA_API_KEY" -X POST
  ```
</CodeGroup>

<Note>
  A domain is an **organization** object, not an identity one — two personas routinely send from the
  same domain, and the system domain belongs to the organization (ADR-0119). Earlier drafts of this
  page nested these calls under `/v1/identities/{id}/domains`; that spelling is not served. An
  identity reaches a domain through the inboxes provisioned on it.
</Note>

## The email readiness gate

The three email-track requirements combine into a single practical rule: **a domain must be verified before an inbox can send.**

| Action               | Gate                                    |
| -------------------- | --------------------------------------- |
| Create an inbox      | `status: active`                        |
| Send from the inbox  | `dns_status: provisioned`               |
| Receive at the inbox | verified for inbound at the email layer |

<Warning>
  A send attempted before the domain finishes verifying is not a hard failure — it returns a **retryable** `job_not_ready`. The [durable runtime](/docs/concepts/runtime) can retry it once verification completes rather than dropping the message.
</Warning>

## Protected DNS records

Some records are managed by Vetta to keep deliverability and inbound routing intact. **The agent cannot edit them:**

* `_dmarc`
* `_domainkey` and `*._domainkey` (DKIM)
* inbound `MX` and their associated `TXT`

On a `system` domain the entire zone is **list-only** — any edit attempt returns `403`. On `external`/`purchased` domains you may manage your other records, but the protected set above is still off-limits to the agent.

```bash CLI theme={"system"}
vetta identity domain records list --domain acme-mail.com   # always allowed
vetta identity domain records set  --domain acme.<vetta-apex> ...   # 403 on system domains
```

## Configuration reference

### Verification track fields

<ResponseField name="status" type="string">
  Email-track lifecycle for the domain as a whole. `pending_dns` → `active`. Creating an inbox requires `active`.
</ResponseField>

<ResponseField name="dns_status" type="string">
  Email DNS (SPF/DKIM) provisioning state. `pending_verification` → `provisioned`. **Sending** requires `provisioned`.
</ResponseField>

<ResponseField name="app_connect_status" type="string">
  App/HTTP track state, independent of the two email fields above. Follows its own `pending` → `connected` lifecycle.
</ResponseField>

### Registrar kinds

A domain's `kind` records where it lives and who owns it:

<ParamField path="system" type="kind">
  Auto-provisioned subdomain under Vetta's shared apex — e.g. `{org-slug}.<vetta-apex>`. Created for you at signup. **List-only**: DNS records are managed by Vetta and cannot be edited (editing returns `403`).
</ParamField>

<ParamField path="external" type="kind">
  Bring-your-own-domain (BYOD). You own the domain at your registrar; Vetta returns the DNS records to add, then verifies them.
</ParamField>

<ParamField path="purchased" type="kind">
  Bought through Vetta. Vetta owns registration and manages the zone on your behalf. Purchase is [approval-gated](/docs/identity/policies) by default.
</ParamField>

<Card title="Next: email" icon="mail" href="/docs/identity/email">
  Provision inboxes on a verified domain and send mail.
</Card>
