## Проверить домен

`POST /v1/infra/servers/:id/domain/verify`

Проверяет обе DNS-записи привязанного домена и, если они верны, включает домен: приложение начинает отвечать по нему. Этим же вызовом возвращают в работу домен, отключённый после неудачной проверки.

Кому доступен вызов — [Доступ](/docs/infra/domain#доступ).

## Параметры

| Параметр | В | Тип | Обяз. | Описание |
|----------|---|-----|:-----:|----------|
| `id` | path | string (UUID) | да | ID сервера. Источник — поле `id` из [списка серверов](/docs/infra/servers/list) |

Тело запроса пустое.

## Примеры

### curl — личный ключ

```bash
curl -X POST "https://vibecode.bitrix24.tech/v1/infra/servers/SERVER_ID/domain/verify" \
  -H "X-Api-Key: YOUR_API_KEY"
```

### curl — OAuth-приложение

```bash
curl -X POST "https://vibecode.bitrix24.tech/v1/infra/servers/SERVER_ID/domain/verify" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN"
```

### JavaScript — личный ключ

```javascript
const res = await fetch(
  `https://vibecode.bitrix24.tech/v1/infra/servers/${serverId}/domain/verify`,
  { method: 'POST', headers: { 'X-Api-Key': 'YOUR_API_KEY' } }
)
const body = await res.json()
if (body.success) {
  console.log(`Домен включён: ${body.data.domain.fqdn}`)
} else if (body.error.code === 'DOMAIN_VERIFICATION_FAILED') {
  // Показать, какая запись не прошла проверку
  for (const record of body.error.details.records) {
    if (record.status !== 'ok') console.log(`${record.type} ${record.host}: ${record.status}`)
  }
} else {
  throw new Error(body.error.code)
}
```

### JavaScript — OAuth-приложение

```javascript
const res = await fetch(
  `https://vibecode.bitrix24.tech/v1/infra/servers/${serverId}/domain/verify`,
  {
    method: 'POST',
    headers: {
      'X-Api-Key': 'YOUR_APP_KEY',
      'Authorization': 'Bearer USER_SESSION_TOKEN',
    },
  }
)
```

## Поля ответа

| Поле | Тип | Описание |
|------|-----|----------|
| `success` | boolean | `true`, когда обе записи верны и домен включён |
| `data.domain.id` | string (UUID) | ID привязки домена |
| `data.domain.fqdn` | string | Имя домена в нижнем регистре |
| `data.domain.isApex` | boolean | `true` — корневой домен, `false` — поддомен |
| `data.domain.status` | string | Статус домена, в этом ответе `ACTIVE`. Все значения — [Статусы домена](/docs/infra/domain#статусы-домена) |
| `data.domain.source` | string | Происхождение домена. `BYO` — домен, которым владеете вы |
| `data.domain.dnsMode` | string | Кто ведёт DNS-записи. `USER_DNS` — вы, у своего DNS-провайдера |
| `data.domain.verifiedAt` | string \| null | Время последней успешной проверки, ISO 8601 |
| `data.domain.lastVerifiedAt` | string \| null | Время последней проверки, успешной или нет, ISO 8601 |
| `data.domain.createdAt` | string | Время привязки домена, ISO 8601 |
| `data.domain.certIssued` | boolean | Выпущен ли сертификат для `https` |
| `data.records` | array | Результат по каждой записи: первой идёт `TXT`, второй — `CNAME` либо `A` |
| `data.records[].type` | string | Тип записи: `TXT`, `CNAME` или `A` |
| `data.records[].host` | string | Полное имя записи |
| `data.records[].status` | string | `ok` — запись верна, `missing` — не найдена, `wrong` — найдена с другим значением. В этом ответе у обеих записей `ok` |
| `data.records[].found` | string[] | Значения, которые проверка прочитала из DNS. У записи `CNAME` здесь могут стоять IP-адреса: так отвечает DNS-провайдер, который сам разворачивает `CNAME` в адреса |

## Пример ответа

```json
{
  "success": true,
  "data": {
    "domain": {
      "id": "bea9db66-411b-432e-a365-283a74f5714f",
      "fqdn": "app.example.com",
      "isApex": false,
      "status": "ACTIVE",
      "source": "BYO",
      "dnsMode": "USER_DNS",
      "verifiedAt": "2026-10-05T12:53:02.655Z",
      "lastVerifiedAt": "2026-10-05T12:53:02.653Z",
      "createdAt": "2026-10-05T12:53:02.616Z",
      "certIssued": false
    },
    "records": [
      {
        "type": "TXT",
        "host": "_vibecode-challenge.app.example.com",
        "status": "ok",
        "found": [
          "vibe-verify=f36ac31f213d1e3630c75f000b6631f5"
        ]
      },
      {
        "type": "CNAME",
        "host": "app.example.com",
        "status": "ok",
        "found": [
          "lb.bitrix24site.ru"
        ]
      }
    ]
  }
}
```

## Пример ответа при ошибке

422 — запись `TXT` верна, но `CNAME` указывает на другой адрес:

```json
{
  "success": false,
  "error": {
    "code": "DOMAIN_VERIFICATION_FAILED",
    "message": "Domain verification failed",
    "details": {
      "reason": "NOT_POINTING_TO_US",
      "records": [
        {
          "type": "TXT",
          "host": "_vibecode-challenge.app.example.com",
          "status": "ok",
          "found": [
            "vibe-verify=f36ac31f213d1e3630c75f000b6631f5"
          ]
        },
        {
          "type": "CNAME",
          "host": "app.example.com",
          "status": "wrong",
          "found": [
            "other.example.net"
          ]
        }
      ],
      "domain": {
        "id": "bea9db66-411b-432e-a365-283a74f5714f",
        "fqdn": "app.example.com",
        "isApex": false,
        "status": "FAILED",
        "source": "BYO",
        "dnsMode": "USER_DNS",
        "verifiedAt": null,
        "lastVerifiedAt": "2026-10-05T12:53:02.648Z",
        "createdAt": "2026-10-05T12:53:02.616Z",
        "certIssued": false
      },
      "hint": "Fix the records whose status is \"missing\" or \"wrong\" (see records), then repeat the check. A record that is already published is read from the authoritative name servers of the zone, so it is seen at once. If the check fails right after you edited DNS, the DNS provider has not published the change yet, or a resolver still returns the previous answer until the record TTL expires — for a name that did not exist before, that is the negative-cache TTL of the zone (SOA minimum, up to an hour). Repeating the check immediately returns the same result: wait a few minutes between attempts. The check is limited to 5 calls per minute."
    }
  }
}
```

Поля `error.details` этого отказа:

| Поле | Тип | Описание |
|------|-----|----------|
| `error.details.reason` | string | Первая непрошедшая запись. `OWNERSHIP_TXT_MISSING` — запись `TXT` не найдена или неверна, `NOT_POINTING_TO_US` — `TXT` верна, но домен не направлен на платформу |
| `error.details.records` | array | Результат по обеим записям, поля те же, что у `data.records`. Пока запись `TXT` не подтверждена, `found` пуст у обеих записей |
| `error.details.records[].hint` | string | Приходит не у каждой записи. `DOUBLED_ZONE` — записи нет, но она нашлась под именем, к которому зона дописана дважды: в панели введено `host` вместо `name`, см. [DNS-записи](/docs/infra/domain#dns-записи). `HAS_AAAA` — на имени домена есть запись `AAAA`, удалите её |
| `error.details.domain` | object | Домен после проверки, поля те же, что у `data.domain`. Статус — `FAILED` |
| `error.details.hint` | string | Текст на английском: что исправить и когда повторять проверку |

## Ошибки

| HTTP | Код | Описание |
|------|-----|----------|
| 400 | `FQDN_FORBIDDEN` | Обе записи верны, но имя домена не допускается: оно не должно содержать `bitrix`. Домен переходит в `FAILED` |
| 401 | `MISSING_API_KEY` | Не передан заголовок `X-Api-Key` |
| 401 | `INVALID_API_KEY` | Ключ не опознан: такой строки на платформе нет |
| 403 | `INFRA_SCOPE_REQUIRED` | У ключа нет скоупа `vibe:infra`. Проверяется раньше, чем поиск сервера |
| 403 | `INFRA_FORBIDDEN_FOR_COWORK_KEY` | Вызов сделан ключом Cowork/Code — такой ключ работает только с данными, изменяющие операции ему закрыты. Что делать — [Проектный ключ для деплоя](/docs/cowork/deploy-key) |
| 403 | `WRITE_BLOCKED_READONLY_KEY` | Ключ в режиме «только чтение» — изменяющие операции ему закрыты |
| 403 | `CUSTOM_DOMAINS_DISABLED` | Свои домены не включены для владельца сервера. Условия — [Доступ](/docs/infra/domain#доступ) |
| 403 | `CUSTOM_DOMAINS_ZONE_UNSUPPORTED` | Регион аккаунта Битрикс24 не поддерживает свои домены |
| 403 | `CUSTOM_DOMAINS_REQUIRE_COMMERCIAL` | У аккаунта Битрикс24 нет коммерческого тарифа |
| 404 | `SERVER_NOT_FOUND` | Сервер не найден, удалён либо им управляет другой ключ |
| 404 | `NO_DOMAIN` | К серверу не привязан домен. Проверяется раньше условий доступа с кодом `403` |
| 409 | `SERVER_HAS_NO_APP_HOST` | Обе записи верны, но у сервера ещё нет публичного адреса приложения. Статус домена не меняется. Разверните приложение и повторите проверку |
| 409 | `DOMAIN_TAKEN_EXTERNAL` | Обе записи верны, но после привязки имя подключили к другому продукту Битрикс24. Домен переходит в `FAILED`. Отключите имя там и повторите проверку |
| 422 | `DOMAIN_VERIFICATION_FAILED` | Запись не найдена или неверна. Домен переходит в `FAILED`. Какая запись не прошла — в `error.details.records` |
| 429 | `RATE_LIMITED` | Превышен лимит 5 запросов в минуту на этот эндпоинт. Лимит считается на владельца ключа, а не на отдельный ключ. Точное значение — в заголовке `x-ratelimit-limit` (потолок делится на реплики) |
| 503 | `CUSTOM_DOMAIN_REGISTRY_UNAVAILABLE` | Реестр доменов не ответил, либо платформа не смогла прочитать адрес, с которым сравнивает записи. Статус домена остаётся прежним. Повторите вызов позже |
| 503 | `user_self_deletion_pending` | Владелец ключа запросил удаление своего аккаунта на портале. Доступ вернётся после отмены удаления |

Полный список общих ошибок API — [Ошибки](/docs/errors).

## Известные особенности

- **Повторная проверка сразу после `422` вернёт тот же результат.** Запись, которую DNS-провайдер уже опубликовал, проверка читает с авторитетных серверов зоны и видит сразу. Если отказ пришёл сразу после правки DNS, провайдер ещё не опубликовал изменение либо кэширующий DNS-сервер отдаёт прежний ответ, пока не истечёт время жизни записи (TTL). Для имени, которого раньше не существовало, это до часа. Делайте паузу в несколько минут между попытками.
- **Проверка работающего домена может его отключить.** Вызов допустим и для домена в статусе `ACTIVE`. Если записи к этому моменту неверны, домен переходит в `FAILED`, и приложение перестаёт отвечать по нему до следующей успешной проверки.
- **Сертификат выпускается после ответа.** Когда домен приходит в `ACTIVE` из другого статуса, `certIssued` равен `false`. Готовность `https` показывает [`GET /v1/infra/servers/:id/domain`](./get.md).

## Смотрите также

- [Привязать домен](./attach.md)
- [Получить домен](./get.md)
- [Отвязать домен](./detach.md)
- [Свой домен](/docs/infra/domain)
