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

# Domains

> Attach custom domains to a workspace and manage DNS-based verification.

export const ErrorTable = ({errors}) => <div className="gv-error-table" role="table" aria-label="Errors and fixes">
    <div className="gv-error-table__head" role="row">
      <span className="gv-error-table__col-head" role="columnheader">Error</span>
      <span className="gv-error-table__col-head" role="columnheader">Cause</span>
      <span className="gv-error-table__col-head" role="columnheader">Fix</span>
    </div>
    {errors.map((err, i) => <div className="gv-error-table__row" role="row" key={i}>
        <code className="gv-error-table__code" role="cell">{err.code}</code>
        <div className="gv-error-table__cause" role="cell">{err.cause}</div>
        <div className="gv-error-table__fix" role="cell">{err.fix}</div>
      </div>)}
  </div>;

export const Def = ({term, type, defaultValue, required, children}) => <>
    <dt className="gv-def-list__term">
      <span className="gv-def-list__name">{term}</span>
      {type && <span className="gv-def-list__type">{type}</span>}
      {required && <span className="gv-def-list__required">required</span>}
      {defaultValue !== undefined && <span className="gv-def-list__default">
          default <code>{defaultValue}</code>
        </span>}
    </dt>
    <dd className="gv-def-list__description">{children}</dd>
  </>;

export const DefList = ({children}) => <dl className="gv-def-list">{children}</dl>;

export const ExpectedOutput = ({caption = "Expected output", children}) => <div className="gv-output">
    <div className="gv-output__label">
      <svg className="gv-output__icon" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
        <polyline points="15 10 20 15 15 20" />
        <path d="M4 4v7a4 4 0 0 0 4 4h12" />
      </svg>
      <span>{caption}</span>
    </div>
    <div className="gv-output__body">{children}</div>
  </div>;

export const APIBadge = ({method = "GET", path, scope}) => {
  const m = method.toUpperCase();
  return <span className="gv-api-badge">
      <span className={`gv-api-badge__method gv-api-badge__method--${m.toLowerCase()}`}>
        {m}
      </span>
      <code className="gv-api-badge__path">{path}</code>
      {scope && <span className="gv-api-badge__scope">
          <span className="gv-api-badge__scope-label">scope</span>
          <code>{scope}</code>
        </span>}
    </span>;
};

export const NumberedStep = ({n, title, children}) => <li className="gv-step">
    <span className="gv-step__index">{n}</span>
    <div className="gv-step__body">
      <h3 className="gv-step__title">{title}</h3>
      <div className="gv-step__content">{children}</div>
    </div>
  </li>;

export const NumberedSteps = ({children}) => <ol className="gv-steps">{children}</ol>;

export const Eyebrow = ({label, tone = "default"}) => <div className={`gv-eyebrow gv-eyebrow--${tone}`}>
    <span className="gv-eyebrow__bar" aria-hidden="true" />
    <span className="gv-eyebrow__label">{label}</span>
  </div>;

<Eyebrow label="RESOURCE" />

Every workspace is reachable by default at `{slug}.gavai.app`. The Domains resource attaches a hostname you control so the workspace serves at a domain of your choosing. Verification is DNS-based: you attach the domain, gavAI returns a DNS record to set, and you trigger a check once propagation completes. By the end of this page you will know the attach-and-verify flow, the status values a domain moves through, and which endpoints drive each transition.

## The flow

<NumberedSteps>
  <NumberedStep n={1} title="Attach the domain">
    Call <APIBadge method="POST" path="/v1/workspaces/{slug}/domains" scope="domains:write" /> with the hostname. The response carries a `verification` block with the DNS record values to set.
  </NumberedStep>

  <NumberedStep n={2} title="Set the DNS record">
    At your DNS provider, create the record using `verification.type`, `verification.name`, and `verification.value`. Propagation typically takes a few minutes; longer with high TTLs.
  </NumberedStep>

  <NumberedStep n={3} title="Trigger verification">
    Call <APIBadge method="POST" path="/v1/workspaces/{slug}/domains/{id}/verify" scope="domains:write" /> once you believe DNS has propagated. gavAI performs a live lookup immediately.
  </NumberedStep>

  <NumberedStep n={4} title="Poll status until verified">
    Call <APIBadge method="GET" path="/v1/workspaces/{slug}/domains/{id}/status" scope="domains:read" /> — it reports the result without triggering a fresh lookup. Poll on a short interval while `pending`.
  </NumberedStep>
</NumberedSteps>

## Endpoints

<APIBadge method="GET" path="/v1/workspaces/{slug}/domains" scope="domains:read" />

List all domains attached to the workspace.

<APIBadge method="POST" path="/v1/workspaces/{slug}/domains" scope="domains:write" />

Attach a custom hostname to the workspace.

<APIBadge method="DELETE" path="/v1/workspaces/{slug}/domains/{id}" scope="domains:destructive" />

Detach a domain from the workspace.

<APIBadge method="POST" path="/v1/workspaces/{slug}/domains/{id}/verify" scope="domains:write" />

Trigger a DNS verification check.

<APIBadge method="GET" path="/v1/workspaces/{slug}/domains/{id}/status" scope="domains:read" />

Fetch the current verification status.

## Verification record shape

<ExpectedOutput>
  ```json theme={null}
  {
    "id":       "dom_01H...",
    "hostname": "app.example.com",
    "status":   "pending",
    "verification": {
      "type":  "CNAME",
      "name":  "_gavai-verify.app.example.com",
      "value": "verify.gavai.app"
    },
    "created_at": "2026-03-12T10:00:00Z"
  }
  ```
</ExpectedOutput>

## Status values

<DefList>
  <Def term="pending">Attached but the verification record has not been observed yet.</Def>
  <Def term="verified">The DNS lookup found the expected record. Traffic on the hostname routes to the workspace.</Def>
  <Def term="failed">Verification was attempted and the record is definitively wrong. Fix the DNS record and call verify again.</Def>
</DefList>

<Warning>
  Detaching a verified domain takes effect immediately. If you have live traffic on the hostname, route it elsewhere before calling `DELETE`.
</Warning>

## Common errors

<ErrorTable
  errors={[
{ code: "404 not_found", cause: "No domain with this id in this workspace.", fix: "Verify the id; list `/domains` to see what is attached." },
{ code: "403 insufficient_scope", cause: "Token lacks the required scope for the operation.", fix: "Mint a token with `domains:read`, `domains:write`, or `domains:destructive` as appropriate." },
]}
/>

## Related

<Card title="Custom domains guide" icon="globe" href="/guides/custom-domains">
  Step-by-step walkthrough with DNS provider examples and troubleshooting.
</Card>
