Add a custom domain

POST /v1/custom-domains · permission custom-domains:write

Adds a custom login domain to the environment. The name is converted to its IDNA2008 A-label; public suffixes, single-label names, IP addresses and platform domains are rejected. A name is unique across the platform: if another environment uses it, or it was released less than 30 days ago (quarantine), the request fails with 409 custom_domain_taken. The domain starts in pending_dns; publish the returned dns_records (a permanent TXT ownership record and a CNAME to the environment's edge host) at your DNS provider, then call :verify or wait for the automatic check.

Request

JSON body (unknown fields are rejected with 400 unknown_field):

Field Type Required Description
Idempotency-Key header yes Required. 16-64 characters [A-Za-z0-9_-]; repeating the same key with the same name returns the same domain.
hostname string yes The domain to add (Unicode allowed), at most 253 bytes, for example login.example.com.

Response

201 Created with the domain and an ETag header.

{
  "id": "cdom_01j9xk3m7q8r2s4t6v8w0y2z4a",
  "hostname": "login.example.com",
  "is_primary": false,
  "status": "pending_dns",
  "failure_reason": null,
  "dns_records": [
    {
      "type": "TXT",
      "name": "_akp-verify.login.example.com",
      "value": "idp-verify=3q2-7wEXAMPLEtokenQ9"
    },
    {
      "type": "CNAME",
      "name": "login.example.com",
      "value": "acme.eu.edge.keenauth.app"
    }
  ],
  "certificate_expires_at": null,
  "last_checked_at": null,
  "next_check_at": "2026-10-05T09:36:00Z",
  "activated_at": null,
  "degraded_since": null,
  "status_changed_at": "2026-10-05T09:30:00.123456Z",
  "created_at": "2026-10-05T09:30:00.123456Z",
  "updated_at": "2026-10-05T09:30:00.123456Z"
}

Errors

HTTP code When
400 malformed_request Idempotency-Key is missing or malformed.
400 unknown_field The body contains an unknown field.
403 quota_exceeded The environment's custom domain quota is full, or the environment is a development environment.
409 custom_domain_taken The name is used by another environment or is in its 30-day quarantine.
409 conflict The account or the environment is not active.
415 unsupported_media_type The content type is not application/json.
422 validation_failed hostname is not a valid domain name for a custom domain.
422 idempotency_key_reuse The Idempotency-Key was already used with a different body.
429 rate_limited Too many custom domain add attempts for the account today; retry after the Retry-After period.
503 service_unavailable The platform domain registry or the environment's account information is temporarily unavailable.
401 unauthenticated The credential is missing, invalid or revoked.
403 insufficient_permissions The credential lacks the required permission.

Example

curl -X POST https://api.eu.keenauth.com/v1/custom-domains \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"hostname":"login.example.com"}'