> ## 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.

# Domains

> Org-level sending and receiving domains, their DNS records, and the three readiness tracks.

> [!NOTE]
>
> Domains are part of [Identity](https://vetta.sh/docs/identity/overview.md). A domain belongs to the **organization**, not to one persona — two identities can send from the same domain.

A domain is what makes an [email inbox](https://vetta.sh/docs/api/messaging.md) deliverable. Connect a domain you own (`external`) or mint the organization's system subdomain (`system`), then publish the DNS records the API hands back.

## Three readiness tracks

Verification is not one boolean. A domain advances along three independent tracks, and each gates a different capability:

| Field | Gates | Values |
| - | - | - |
| `dns_status` | **Sending** mail (SPF/DKIM) | `pending_verification`, `provisioned`, `failed` |
| `inbound_status` | **Receiving** mail (MX) | `pending`, `verified`, `failed` |
| `app_connect_status` | HTTP/app hosting | `pending`, `connected`, `failed` |

A send is refused with a retryable `job_not_ready` until `dns_status` is `provisioned`. Receiving is unaffected by `dns_status` and waits on `inbound_status`.

## The domain object

**id** `string`

Unique id (e.g. `dom_01H...`).

**object** `string`

Always `domain`.

**name** `string`

The fully-qualified name, lowercase, no trailing dot.

**kind** `string`

`external` (a domain you own), `system` (a subdomain minted for the org), or `purchased` (bought with Naive credits).

**status** `string`

Overall lifecycle state of the domain record — `pending_dns`, `active`, or `failed`.

**dns\_status** `string`

Mail-sending track. See the table above.

**inbound\_status** `string`

Mail-receiving track.

**app\_connect\_status** `string`

App/HTTP track.

**records** `object[]`

The DNS records to publish. Same shape as [List DNS records](#list-dns-records).

**last\_verified\_at** `string | null`

When verification last ran.

**created\_at** `string`

Creation timestamp.

## Connect a domain

`POST /v1/domains` — scope `agents:write`

Idempotent on `name`: connecting a domain that already exists returns the existing record rather than a duplicate.

**kind** `string`

`external` (a domain you own) or `system` (a subdomain minted under the org slug). Defaults to `external`. Purchases use the separately privileged endpoint below.

**name** `string`

The domain to connect. Required for `kind: "external"`; omit for `system` and the API mints the subdomain.

**receive** `boolean`

Also request inbound (MX) records. Defaults to `true`.

```bash cURL theme={"system"}
curl -fsSL https://api.vetta.sh/v1/domains \
  -H "authorization: Bearer sk_live_..." \
  -H "content-type: application/json" \
  -d '{ "kind": "external", "name": "mail.acme.dev", "receive": true }'
```

```json Response theme={"system"}
{
  "id": "dom_01H9DM...",
  "object": "domain",
  "name": "mail.acme.dev",
  "kind": "external",
  "status": "pending_dns",
  "dns_status": "pending_verification",
  "inbound_status": "pending",
  "app_connect_status": "pending",
  "records": [
    { "type": "TXT", "host": "@", "value": "v=spf1 include:...", "priority": null, "managed": true }
  ],
  "last_verified_at": null,
  "created_at": "2026-08-20T17:00:00Z"
}
```

**Errors** — `validation_failed` (400) when `name` is not a valid FQDN or `kind` is unknown; `insufficient_scope` (403) without `agents:write`.

## Buy a domain with Naive credits

First call `POST /v1/domains/quote` with `{ "name": "example.com" }` using `billing:read`.
It returns authoritative availability plus `registration_price_micro_usd` and
`renewal_price_micro_usd`. Quoting does not reserve the domain or move credits.

Then call `POST /v1/domains/purchase` using `billing:write`, an `Idempotency-Key`, and:

```json theme={"system"}
{ "name": "example.com", "max_price_micro_usd": 12000000, "receive": true }
```

The API rechecks availability and price, refuses premium domains, and will not buy if the live
price exceeds the explicit ceiling. It debits the organization's Naive-credit balance before the
registrar call. The platform registrar account funds the external purchase; this endpoint neither
accepts nor forwards a customer card. Automatic renewal is off.

A completed registration returns `201`. A registration still processing returns `202` with
`Cache-Control: no-store`; retry the same request with the same idempotency key to reconcile it.
You do not have to: the platform checks every pending purchase about every 10 minutes and finishes
it. A confirmed registrar rejection refunds the debit. An ambiguous outcome stays pending, because
completed registrations are non-refundable and an eager refund could give away a domain. If no
registration exists a day later, the debit is refunded.

A refused or refunded attempt never blocks the next one. Top up after a `402` and buy again. Delete
a failed purchase and buy again with a new `Idempotency-Key`; the new attempt is charged on its own.
An `Idempotency-Key` names one attempt: reusing the key of a refunded attempt is refused, and can
never register a free domain.

**Errors** — `insufficient_credits` (402); `validation_failed` (400) for an unavailable domain
(including one another organization is buying), missing idempotency key, or a price above the
ceiling; `state_conflict` (409) when the registrar rejected the purchase (credits refunded), when a
failed purchase of the name must be deleted first, when the `Idempotency-Key` belongs to a finished
attempt, or when another request for the name is still
starting (retry shortly); `feature_not_configured` (501) when the platform registrar account is not
configured.

## List domains

`GET /v1/domains` — scope `agents:read`

**limit** `integer`

Page size. See [Pagination](https://vetta.sh/docs/api/pagination.md).

**after** `string`

Cursor from the previous page.

Returns a [paginated list](https://vetta.sh/docs/api/pagination.md) of domain objects.

## Retrieve a domain

`GET /v1/domains/{id}` — scope `agents:read`

Returns the domain object. `not_found` (404) if the id does not belong to your organization.

## Verify a domain

`POST /v1/domains/{id}/verify` — scope `agents:write`

Re-runnable. Each readiness track advances on its own, so calling `verify` after publishing only the SPF/DKIM records moves `dns_status` and leaves `inbound_status` where it was. The **domain object** comes back as the answer — read the three status fields to see what changed.

```bash cURL theme={"system"}
curl -fsSL -X POST https://api.vetta.sh/v1/domains/dom_01H9DM.../verify \
  -H "authorization: Bearer sk_live_..."
```

**Errors** — `not_found` (404); `insufficient_scope` (403). A track that is still unpublished is not an error: it simply stays `pending`.

## List DNS records

`GET /v1/domains/{id}/records` — scope `agents:read`

Always allowed on every `kind` — reading the zone is never the dangerous half.

**type** `string`

`TXT`, `CNAME`, or `MX`.

**host** `string`

The record host, relative to the registrable zone.

**value** `string`

The record value.

**priority** `integer | null`

MX priority; `null` for `TXT` and `CNAME`.

**managed** `boolean`

`true` for records the platform owns — DKIM, `_dmarc`, and the inbound MX. An agent may read a managed record and must never edit or delete it: removing the DKIM record breaks the signature on its own mail.

```json Response theme={"system"}
{
  "object": "list",
  "data": [
    { "type": "MX",    "host": "@",                "value": "inbound.vetta.sh", "priority": 10,   "managed": true },
    { "type": "TXT",   "host": "_dmarc",           "value": "v=DMARC1; p=none", "priority": null, "managed": true },
    { "type": "CNAME", "host": "sel._domainkey",   "value": "sel.dkim...",      "priority": null, "managed": true }
  ]
}
```

## Delete a domain

`DELETE /v1/domains/{id}` — scope `agents:write`

Returns the standard deleted envelope. Inboxes on the domain stop delivering; released addresses are not reusable until the domain is reconnected and re-verified.

```json Response theme={"system"}
{ "id": "dom_01H9DM...", "object": "domain", "deleted": true }
```

> [card] [Email & SMS](https://vetta.sh/docs/api/messaging.md)
>
> Give a persona an inbox on this domain.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.