---
title: "Register an identity domain to prove (owner)"
method: POST
path: "/v1/organizations/{organization_id}/sso/domains"
tags: ["organizations"]
---

# Register an identity domain to prove (owner)

`POST /v1/organizations/{organization_id}/sso/domains`

## Request

- Base URL: `https://api.ethiack.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.ethiack.com/v1/organizations/{organization_id}/sso/domains`
- Auth: none declared

Register a domain and return the DNS record that proves it.

The domain is unproven until a check observes the record; nothing here can
shortcut that. The response carries the token to publish, so a caller never
needs a second request to find out what to do next.

Args:
    organization_id (int): The parent organization.
    payload (IdentityDomainRequest): The domain to prove.
    db (AsyncSession): Request-scoped session.
    context (ActiveContext): Resolved caller.

Returns:
    IdentityDomainResponse: The registered domain and its challenge record.

## Path parameters

- `organization_id` integer, required

## Request body

- IdentityDomainRequest: A domain the organization claims to control. Attributes: domain (str): The domain to prove, in any spelling. Case, a trailing dot and Unicode are canonicalised server-side, so a customer pasting ``ACME.com.`` gets the same row as one typing ``acme.com``.
  - `domain` string, required: Domain to prove control of, e.g. acme.com. Case and a trailing dot are normalised.

## Response `201`

Successful Response

- IdentityDomainResponse: A registered domain and everything needed to publish its proof. The two record fields are computed rather than stored. They exist because **the single most common failure in DNS verification is pasting the wrong one of them**: most provider UIs take the name *relative to the zone* and append the domain themselves, so pasting the FQDN into that field yields ``_ethiack-identity-challenge.acme.com.acme.com``, which resolves to nothing and reads as "your check is broken". Returning both, named for which is which, is cheaper than the support conversation. Attributes: id (int): Row id, for the delete route. Re-checking is the background sweep's job and has no endpoint. domain (str): The canonicalised domain. verified_at (datetime | None): When the proof was last observed, or ``None`` while unproven. Cleared when a check no longer finds the record, so this is current state rather than history. last_checked_at (datetime | None): When the domain was last looked up, whatever the outcome. A proof that is set but stale stops admitting: both fields matter, not just the first. record_name (str): Fully-qualified name to create the TXT record at. record_name_relative (str): The same name as most DNS provider UIs want it — relative to the zone, without the domain suffix. record_value (str): The TXT value to publish: the token, bare.
  - `id` integer, required
  - `domain` string, required
  - `verified_at` string, date-time, nullable, required
  - `last_checked_at` string, date-time, nullable, required
  - `record_name` string, required
  - `record_name_relative` string, required
  - `record_value` string, required

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/ethiack/apis/ethiack-api.md) - [All operations](https://skmtc.dev/ethiack/apis/ethiack-api/llms.txt) - [OpenAPI document](https://skmtc.dev/ethiack/apis/ethiack-api/revisions/4eaba89bfebf?raw)
