# at.happyproto.server.addDomain

> Published by [happyproto.at](https://lexicon.garden/identity/did:plc:mo7vq4jbioxwafy5zkcc3eux)

✓ This is the authoritative definition for this NSID.

## Links

- [View on Lexicon Garden](https://lexicon.garden/lexicon/did:plc:mo7vq4jbioxwafy5zkcc3eux/at.happyproto.server.addDomain)
- [Documentation](https://lexicon.garden/lexicon/did:plc:mo7vq4jbioxwafy5zkcc3eux/at.happyproto.server.addDomain/docs)
- [Examples](https://lexicon.garden/lexicon/did:plc:mo7vq4jbioxwafy5zkcc3eux/at.happyproto.server.addDomain/examples)

## Definitions

### `at.happyproto.server.addDomain#altRecord`

**Type**: `object`

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `type` | `string` | Yes | A. |
| `value` | `string` | Yes | The instance host's public IPv4. |

### `at.happyproto.server.addDomain#checks`

**Type**: `object`

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `checkedAt` | `string` | Yes | When the check ran. |
| `ownership` | `string` | Yes | Whether the TXT record carries this claim's token. |
| `pointing` | `string` | No | Whether the hostname resolves to the instance's host. Absent when the check didn't get that far, such as when the TXT record was missing. |

### `at.happyproto.server.addDomain#domain`

**Type**: `object`

A custom domain on one of the caller's instances.

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `checks` | `ref` → `#checks` | No | The latest DNS check. Absent until the first check has run. |
| `createdAt` | `string` | Yes | When the domain was added. |
| `expiresAt` | `string` | No | When a pending claim expires if it is never verified. Absent once verified. |
| `hostname` | `string` | Yes | The hostname, lowercase, internationalized names in punycode. |
| `id` | `string` | Yes | The domain's UUID. |
| `lapsesAt` | `string` | No | When HappyProto stops serving the domain if its TXT record stays missing. Absent while the record is present. |
| `primary` | `boolean` | Yes | Whether this is the instance's primary domain, the one its PUBLIC_URL follows. |
| `records` | `array` | Yes | The DNS records the customer publishes: the TXT that proves ownership, then the CNAME that points the hostname at the instance. |
| `serving` | `string` | Yes | Whether the domain actually works: off until it is verified, issuing while its certificate is ordered, live once it serves over HTTPS. |
| `state` | `string` | Yes | pending: waiting for the DNS records. verified: proven and served. lapsed: the TXT record went missing, and HappyProto stopped serving it; it recovers if the record comes back before expiresAt. |
| `verifiedAt` | `string` | No | When the domain was first verified. |

### `at.happyproto.server.addDomain`

**Type**: `procedure`

Add a custom domain to one of the caller's instances. It starts pending, with the DNS records to publish. Customers only.

#### Input

**Encoding**: `application/json`

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `hostname` | `string` | Yes | The hostname, such as app.example.com. No scheme, port or path. |
| `idempotencyKey` | `string` | Yes | A UUID the client generates once per intended change and resends on every retry of it, so a retry gets the original reply instead of acting twice. |
| `instanceId` | `string` | Yes | The instance's UUID. |

#### Output

**Encoding**: `application/json`

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `domain` | `ref` → `#domain` | Yes |  |

#### Errors

- **AuthRequired**: This endpoint requires an authenticated caller.
- **Forbidden**: The caller is not a HappyProto customer, or the provisioner refused them.
- **InvalidRequest**: instanceId or idempotencyKey is not a UUID, or hostname is missing or too long.
- **NotFound**: No such instance or domain, or it is not the caller's. The cases are indistinguishable.
- **NotAvailable**: Custom domains are not switched on yet.
- **InvalidSlug**: Passed through from the provisioner.
- **SlugTaken**: Passed through from the provisioner.
- **NotEntitled**: Passed through from the provisioner.
- **InstanceLimit**: Passed through from the provisioner.
- **NoCapacity**: Passed through from the provisioner.
- **OperationInProgress**: Passed through from the provisioner: the instance is already changing, or a request with this idempotencyKey is still running.
- **InvalidHostname**: The hostname is not a valid public hostname: no scheme, port, path, wildcard or IP address.
- **PlatformHostname**: The hostname is HappyProto's own, or under one of its domains.
- **DomainExists**: The instance already has this hostname.
- **DomainLimit**: The instance already has as many custom domains as it may.
- **TooSoon**: Passed through from the provisioner.
- **DomainIsPrimary**: Passed through from the provisioner.
- **NotVerified**: Passed through from the provisioner.
- **CertificateNotReady**: Passed through from the provisioner.
- **VersionTooOld**: Passed through from the provisioner.
- **Upstream**: The provisioner is unreachable, not configured, or answered with an error it does not explain to callers, or the job could not be started.

### `at.happyproto.server.addDomain#record`

**Type**: `object`

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `alt` | `ref` → `#altRecord` | No | For an apex name, which cannot hold a CNAME: the A record to publish instead. Absent when the instance's host has no public IPv4. |
| `name` | `string` | Yes | The record's name. |
| `type` | `string` | Yes | TXT or CNAME. |
| `value` | `string` | Yes | The record's value. |

## Raw Schema

```json
{
  "$type": "com.atproto.lexicon.schema",
  "defs": {
    "altRecord": {
      "properties": {
        "type": {
          "description": "A.",
          "type": "string"
        },
        "value": {
          "description": "The instance host's public IPv4.",
          "type": "string"
        }
      },
      "required": [
        "type",
        "value"
      ],
      "type": "object"
    },
    "checks": {
      "properties": {
        "checkedAt": {
          "description": "When the check ran.",
          "type": "string"
        },
        "ownership": {
          "description": "Whether the TXT record carries this claim's token.",
          "knownValues": [
            "ok",
            "missing",
            "error"
          ],
          "type": "string"
        },
        "pointing": {
          "description": "Whether the hostname resolves to the instance's host. Absent when the check didn't get that far, such as when the TXT record was missing.",
          "knownValues": [
            "ok",
            "elsewhere",
            "error"
          ],
          "type": "string"
        }
      },
      "required": [
        "ownership",
        "checkedAt"
      ],
      "type": "object"
    },
    "domain": {
      "description": "A custom domain on one of the caller's instances.",
      "properties": {
        "checks": {
          "description": "The latest DNS check. Absent until the first check has run.",
          "ref": "#checks",
          "type": "ref"
        },
        "createdAt": {
          "description": "When the domain was added.",
          "type": "string"
        },
        "expiresAt": {
          "description": "When a pending claim expires if it is never verified. Absent once verified.",
          "type": "string"
        },
        "hostname": {
          "description": "The hostname, lowercase, internationalized names in punycode.",
          "type": "string"
        },
        "id": {
          "description": "The domain's UUID.",
          "type": "string"
        },
        "lapsesAt": {
          "description": "When HappyProto stops serving the domain if its TXT record stays missing. Absent while the record is present.",
          "type": "string"
        },
        "primary": {
          "description": "Whether this is the instance's primary domain, the one its PUBLIC_URL follows.",
          "type": "boolean"
        },
        "records": {
          "description": "The DNS records the customer publishes: the TXT that proves ownership, then the CNAME that points the hostname at the instance.",
          "items": {
            "ref": "#record",
            "type": "ref"
          },
          "type": "array"
        },
        "serving": {
          "description": "Whether the domain actually works: off until it is verified, issuing while its certificate is ordered, live once it serves over HTTPS.",
          "knownValues": [
            "routing",
            "issuing",
            "live",
            "failed",
            "off"
          ],
          "type": "string"
        },
        "state": {
          "description": "pending: waiting for the DNS records. verified: proven and served. lapsed: the TXT record went missing, and HappyProto stopped serving it; it recovers if the record comes back before expiresAt.",
          "knownValues": [
            "pending",
            "verified",
            "lapsed"
          ],
          "type": "string"
        },
        "verifiedAt": {
          "description": "When the domain was first verified.",
          "type": "string"
        }
      },
      "required": [
        "id",
        "hostname",
        "state",
        "primary",
        "records",
        "serving",
        "createdAt"
      ],
      "type": "object"
    },
    "main": {
      "description": "Add a custom domain to one of the caller's instances. It starts pending, with the DNS records to publish. Customers only.",
      "errors": [
        {
          "description": "This endpoint requires an authenticated caller.",
          "name": "AuthRequired"
        },
        {
          "description": "The caller is not a HappyProto customer, or the provisioner refused them.",
          "name": "Forbidden"
        },
        {
          "description": "instanceId or idempotencyKey is not a UUID, or hostname is missing or too long.",
          "name": "InvalidRequest"
        },
        {
          "description": "No such instance or domain, or it is not the caller's. The cases are indistinguishable.",
          "name": "NotFound"
        },
        {
          "description": "Custom domains are not switched on yet.",
          "name": "NotAvailable"
        },
        {
          "description": "Passed through from the provisioner.",
          "name": "InvalidSlug"
        },
        {
          "description": "Passed through from the provisioner.",
          "name": "SlugTaken"
        },
        {
          "description": "Passed through from the provisioner.",
          "name": "NotEntitled"
        },
        {
          "description": "Passed through from the provisioner.",
          "name": "InstanceLimit"
        },
        {
          "description": "Passed through from the provisioner.",
          "name": "NoCapacity"
        },
        {
          "description": "Passed through from the provisioner: the instance is already changing, or a request with this idempotencyKey is still running.",
          "name": "OperationInProgress"
        },
        {
          "description": "The hostname is not a valid public hostname: no scheme, port, path, wildcard or IP address.",
          "name": "InvalidHostname"
        },
        {
          "description": "The hostname is HappyProto's own, or under one of its domains.",
          "name": "PlatformHostname"
        },
        {
          "description": "The instance already has this hostname.",
          "name": "DomainExists"
        },
        {
          "description": "The instance already has as many custom domains as it may.",
          "name": "DomainLimit"
        },
        {
          "description": "Passed through from the provisioner.",
          "name": "TooSoon"
        },
        {
          "description": "Passed through from the provisioner.",
          "name": "DomainIsPrimary"
        },
        {
          "description": "Passed through from the provisioner.",
          "name": "NotVerified"
        },
        {
          "description": "Passed through from the provisioner.",
          "name": "CertificateNotReady"
        },
        {
          "description": "Passed through from the provisioner.",
          "name": "VersionTooOld"
        },
        {
          "description": "The provisioner is unreachable, not configured, or answered with an error it does not explain to callers, or the job could not be started.",
          "name": "Upstream"
        }
      ],
      "input": {
        "encoding": "application/json",
        "schema": {
          "properties": {
            "hostname": {
              "description": "The hostname, such as app.example.com. No scheme, port or path.",
              "type": "string"
            },
            "idempotencyKey": {
              "description": "A UUID the client generates once per intended change and resends on every retry of it, so a retry gets the original reply instead of acting twice.",
              "type": "string"
            },
            "instanceId": {
              "description": "The instance's UUID.",
              "type": "string"
            }
          },
          "required": [
            "instanceId",
            "hostname",
            "idempotencyKey"
          ],
          "type": "object"
        }
      },
      "output": {
        "encoding": "application/json",
        "schema": {
          "properties": {
            "domain": {
              "ref": "#domain",
              "type": "ref"
            }
          },
          "required": [
            "domain"
          ],
          "type": "object"
        }
      },
      "type": "procedure"
    },
    "record": {
      "properties": {
        "alt": {
          "description": "For an apex name, which cannot hold a CNAME: the A record to publish instead. Absent when the instance's host has no public IPv4.",
          "ref": "#altRecord",
          "type": "ref"
        },
        "name": {
          "description": "The record's name.",
          "type": "string"
        },
        "type": {
          "description": "TXT or CNAME.",
          "type": "string"
        },
        "value": {
          "description": "The record's value.",
          "type": "string"
        }
      },
      "required": [
        "type",
        "name",
        "value"
      ],
      "type": "object"
    }
  },
  "id": "at.happyproto.server.addDomain",
  "lexicon": 1
}
```
