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

# Configure a custom email domain

> Integration guide for Configure a custom email domain

Read the current domain and required DNS records, save an operation ID, then set or validate the domain. These settings use the configured SendGrid account and are shared across live/test data modes. Staging can use the same provider account as production; a test credential does not isolate DNS operations.

## Read domain and DNS state

```bash theme={null}
export GIGSTACK_API_BASE="https://api.gigstack.io/v2"
export TEAM_ID="team_example"
curl --fail-with-body "$GIGSTACK_API_BASE/teams/$TEAM_ID/email-domain" \
  -H "Authorization: Bearer $GIGSTACK_API_KEY"
```

Use a verified personal MCP credential or signed-in Firebase user. Team API/OAuth keys cannot impersonate their creator. Current unblocked members can read state; writes and operation recovery require a current team or billing administrator.

Inspect `data.domain.configured`, `data.domain.valid`, `data.domain.dns_setup` and `data.active_operation`. A configured domain may still need DNS changes. Each DNS entry includes `host`, `type`, `data` and `valid`. Install the supplied records through your DNS administrator. The historical `full_domain` display value uses `subdomain@domain`; it is not a website URL.

## Set a domain with a saved operation ID

Generate a UUID v4 and save it before sending the change:

```bash theme={null}
OPERATION_ID="$(python3 -c 'import uuid; print(uuid.uuid4())')"
printf '%s\n' "$OPERATION_ID"
curl --fail-with-body -X PUT "$GIGSTACK_API_BASE/teams/$TEAM_ID/email-domain" \
  -H "Authorization: Bearer $GIGSTACK_API_KEY" \
  -H 'Content-Type: application/json' \
  -d "{\"operation_id\":\"$OPERATION_ID\",\"domain\":\"example.com\",\"subdomain\":\"mail\"}"
```

The server normalizes DNS names to lowercase/IDNA and validates labels. Domain and subdomain each allow at most 253 characters; labels allow 1–63 letters, digits or hyphens, without leading/trailing hyphens. The domain must include a dot. Do not submit an email address, URL or provider key.

A repeated operation ID with the identical action/body returns the same operation. Different input under that ID returns a conflict. Replacing a configured domain retains the old provider domain; `result.previous_provider_domain_retained` reports this. `completed` means setup completed, not that DNS is valid.

## Validate or remove the current domain

After installing DNS, send `POST /teams/{id}/email-domain/validate` with a **new** saved `operation_id`. Inspect `result.domain.valid` and individual DNS records.

To remove the current configuration, send `DELETE /teams/{id}/email-domain` with a new `operation_id`. The server removes the exact stored provider domain and clears local state only after confirmation. It does not sweep previously retained provider domains.

## Ownership verification

`data.ownership_status` is `verified` or `migration_required`. Unverified legacy
configuration remains readable with `can_manage:false`. Mutations and provider
reconciliation return HTTP409 `domain_migration_required` until an operator
verifies ownership independently of historical client-writable IDs. Do not clear
or rewrite team fields to bypass this state. Provider bindings and locks now live
in server-only records; browser-visible team fields are read mirrors.

## Recover an interrupted operation

Read `GET /teams/{id}/email-domain/operations/{operationId}` after a timeout. A `failed` or `outcome_unknown` operation is unsuccessful even if HTTP status is `200`; preserve the response for recovery. Times are milliseconds.

Send `POST /operations/{operationId}/reconcile` under the email-domain base with `{}` to check exact local/provider evidence. This does not repeat a provider write. An optional `observation` describes current state with `attempt_confirmed: false`; it cannot prove that the original validation happened. A lost creation response is not confirmed by finding a same-name domain.

An uncertain operation holds a team lock shared by the API and dashboard. After ten minutes, a current administrator can explicitly send `{"acknowledge_unconfirmed_effects":true}` to `POST /operations/{operationId}/resolve`. This releases the lock for a **new** operation, preserves the old unknown result, and does not undo remote effects. Never resolve and retry automatically.

`401` requires personal authentication; `403` requires current authority. For `409 domain_migration_required`, request operator ownership verification; waiting or retrying does not resolve it. Other `409` responses can require inspecting the existing operation or waiting until recovery is eligible. These routes have offline coverage; live provider validation remains subject to the actual authorized environment and effects.


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