{
  "openapi": "3.1.0",
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "info": {
    "title": "Billing ITGroup Reseller API",
    "version": "1.0.0",
    "description": "REST API для интеграции биллинга реселлера. Контракт описывает HTTP-запросы и ответы; HTTP 202 означает постановку операции в очередь, а не завершение действия в реестре. Пошаговые сценарии, ограничения и обработка неопределённых результатов опубликованы в руководстве и /llms-full.txt. Изменяющие запросы должны следовать описанным правилам идемпотентности. Примеры содержат только синтетические идентификаторы."
  },
  "servers": [
    {
      "url": "https://api.b.websoft.kz/api/reseller/v1",
      "description": "Адрес API, заданный DOCS_API_URL"
    }
  ],
  "paths": {
    "/context": {
      "get": {
        "operationId": "resellerContext",
        "tags": [
          "Контекст"
        ],
        "summary": "Контекст ключа и реселлера",
        "description": "Дополнительный scope не требуется. Возвращает метаданные текущего ключа, включая среду, scopes и настроенный rate_limit_per_minute. Не возвращает секрет, checkout_mode или остаток средств. Лимит нельзя изменять через этот endpoint.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreContextEnvelope"
                },
                "examples": {
                  "live": {
                    "summary": "Рабочий ключ",
                    "value": {
                      "data": {
                        "provider": {
                          "uuid": "019a0000-0000-7000-8000-000000000001",
                          "code": "example-reseller",
                          "name": "Example Reseller",
                          "status": "active",
                          "operation_mode": "hybrid",
                          "customer_mode": "managed",
                          "currency": "KZT",
                          "locale": "ru",
                          "timezone": "Asia/Almaty"
                        },
                        "credential": {
                          "uuid": "019a0000-0000-7000-8000-000000000002",
                          "name": "External billing",
                          "environment": "live",
                          "scopes": [
                            "catalog.read",
                            "quotes.create",
                            "orders.create",
                            "orders.read",
                            "domains.register",
                            "domains.read",
                            "customers.read",
                            "customers.write",
                            "contacts.read",
                            "contacts.write"
                          ],
                          "rate_limit_per_minute": 60,
                          "expires_at": null
                        }
                      }
                    }
                  },
                  "test": {
                    "summary": "Тестовый ключ",
                    "value": {
                      "data": {
                        "provider": {
                          "uuid": "019a0000-0000-7000-8000-000000000001",
                          "code": "example-reseller",
                          "name": "Example Reseller",
                          "status": "active",
                          "operation_mode": "hybrid",
                          "customer_mode": "managed",
                          "currency": "KZT",
                          "locale": "ru",
                          "timezone": "Asia/Almaty"
                        },
                        "credential": {
                          "uuid": "019a0000-0000-7000-8000-000000000002",
                          "name": "External billing",
                          "environment": "test",
                          "scopes": [
                            "catalog.read",
                            "quotes.create",
                            "orders.create",
                            "orders.read",
                            "domains.register",
                            "domains.read",
                            "customers.read",
                            "customers.write",
                            "contacts.read",
                            "contacts.write"
                          ],
                          "rate_limit_per_minute": 60,
                          "expires_at": null
                        }
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Параметры не прошли валидацию или не выполнены бизнес-условия."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Параметры не прошли валидацию или не выполнены бизнес-условия.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ],
        "x-required-scopes": []
      }
    },
    "/catalog/products": {
      "get": {
        "operationId": "listProducts",
        "tags": [
          "Каталог"
        ],
        "summary": "Опубликованные продукты",
        "description": "Список provider_offerings со status=published и product_id, по sort_order. uuid относится к предложению; в quotes используйте resource_uuid. Цены не возвращаются: рассчитывайте через POST /quotes. Каталог не проверяет наличие test-интеграции; test заказы хостинга запрещены. Пагинация cursor/per_page (по умолчанию 25, фактически ограничивается 1..100). Не поддерживаются page, offset, sort, include и произвольные фильтры. При следующем запросе передавайте исходные фильтры вместе с cursor; ссылки не гарантируют сохранение фильтров.",
        "x-required-scope": "catalog.read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreProductPage"
                },
                "examples": {
                  "last_page": {
                    "summary": "Последняя страница продуктов",
                    "value": {
                      "data": [
                        {
                          "uuid": "019a0000-0000-7000-8000-000000000003",
                          "resource_uuid": "019a0000-0000-7000-8000-000000000004",
                          "code": "hosting-standard",
                          "type": "hosting",
                          "name": "Hosting Standard",
                          "description": "Хостинг для одного сайта",
                          "allowed_periods": [
                            {
                              "unit": "month",
                              "count": 1
                            }
                          ],
                          "allowed_operations": [
                            "order"
                          ],
                          "retail_pricing_mode": "provider_fixed"
                        }
                      ],
                      "meta": {
                        "next_cursor": null,
                        "per_page": 25
                      }
                    }
                  },
                  "empty": {
                    "summary": "Нет записей в текущем контексте",
                    "value": {
                      "data": [],
                      "meta": {
                        "next_cursor": null,
                        "per_page": 25
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Параметры не прошли валидацию или не выполнены бизнес-условия."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Параметры не прошли валидацию или не выполнены бизнес-условия.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ]
      }
    },
    "/catalog/domain-zones": {
      "get": {
        "operationId": "listDomainZones",
        "tags": [
          "Каталог"
        ],
        "summary": "Опубликованные доменные зоны",
        "description": "Опубликованные предложения с domain_zone_id, по sort_order. Флаги зоны не заменяют проверку capabilities и маршрутизации регистратора. Цены запрашиваются через quotes, resource_uuid означает UUID зоны. Пагинация cursor/per_page (по умолчанию 25, фактически ограничивается 1..100). Не поддерживаются page, offset, sort, include и произвольные фильтры. При следующем запросе передавайте исходные фильтры вместе с cursor; ссылки не гарантируют сохранение фильтров.",
        "x-required-scope": "catalog.read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreZonePage"
                },
                "examples": {
                  "last_page": {
                    "summary": "Последняя страница зон",
                    "value": {
                      "data": [
                        {
                          "uuid": "019a0000-0000-7000-8000-000000000005",
                          "resource_uuid": "019a0000-0000-7000-8000-000000000006",
                          "zone": "kz",
                          "name": ".kz",
                          "registration_enabled": true,
                          "renewal_enabled": true,
                          "transfer_enabled": true,
                          "requires_eds_verification": false,
                          "requires_edu_license": false,
                          "min_registration_years": 1,
                          "max_registration_years": 10,
                          "allowed_periods": [
                            {
                              "unit": "year",
                              "count": 1
                            }
                          ],
                          "allowed_operations": [
                            "register",
                            "renew",
                            "transfer",
                            "restore"
                          ],
                          "retail_pricing_mode": "provider_fixed"
                        }
                      ],
                      "meta": {
                        "next_cursor": null,
                        "per_page": 25
                      }
                    }
                  },
                  "empty": {
                    "summary": "Нет записей в текущем контексте",
                    "value": {
                      "data": [],
                      "meta": {
                        "next_cursor": null,
                        "per_page": 25
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Параметры не прошли валидацию или не выполнены бизнес-условия."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Параметры не прошли валидацию или не выполнены бизнес-условия.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ]
      }
    },
    "/quotes": {
      "post": {
        "operationId": "createQuote",
        "tags": [
          "Котировки"
        ],
        "summary": "Рассчитать и зафиксировать котировку",
        "description": "Котировка фиксирует цену и payload, но не резервирует доменное имя и не означает исполнение. Срок по умолчанию 15 минут, источник истины expires_at. Исполнять должен тот же bearer-клиент, тот же customer и environment. Нельзя передать свою цену, скидку или сумму. GET/DELETE котировки и изменение выданной котировки не реализованы. Для доменных renew/restore используйте target UUID; restore проверяет период восстановления даже для локально удалённого домена. Все данные ограничены текущим реселлером и средой ключа test/live. Смена среды параметром запроса не поддерживается.",
        "x-required-scope": "quotes.create",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "201": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreQuoteEnvelope"
                },
                "examples": {
                  "registration": {
                    "summary": "Регистрация на год",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000020",
                        "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                        "status": "active",
                        "environment": "live",
                        "currency": "KZT",
                        "issued_at": "2026-10-05T09:00:00+00:00",
                        "expires_at": "2026-10-05T09:15:00+00:00",
                        "items": [
                          {
                            "uuid": "019a0000-0000-7000-8000-000000000021",
                            "resource_type": "domain_zone",
                            "operation": "register",
                            "period_unit": "year",
                            "period_count": 1,
                            "quantity": 1,
                            "currency": "KZT",
                            "unit_price_minor": 700000,
                            "subtotal_minor": 700000,
                            "discount_minor": 0,
                            "tax_minor": 0,
                            "total_minor": 700000
                          }
                        ],
                        "total_minor": 700000
                      }
                    }
                  },
                  "renewal": {
                    "summary": "Продление на год",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000020",
                        "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                        "status": "active",
                        "environment": "live",
                        "currency": "KZT",
                        "issued_at": "2026-10-05T09:00:00+00:00",
                        "expires_at": "2026-10-05T09:15:00+00:00",
                        "items": [
                          {
                            "uuid": "019a0000-0000-7000-8000-000000000021",
                            "resource_type": "domain_zone",
                            "operation": "renew",
                            "period_unit": "year",
                            "period_count": 1,
                            "quantity": 1,
                            "currency": "KZT",
                            "unit_price_minor": 700000,
                            "subtotal_minor": 700000,
                            "discount_minor": 0,
                            "tax_minor": 0,
                            "total_minor": 700000
                          }
                        ],
                        "total_minor": 700000
                      }
                    }
                  },
                  "hosting": {
                    "summary": "Хостинг на месяц",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000020",
                        "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                        "status": "active",
                        "environment": "live",
                        "currency": "KZT",
                        "issued_at": "2026-10-05T09:00:00+00:00",
                        "expires_at": "2026-10-05T09:15:00+00:00",
                        "items": [
                          {
                            "uuid": "019a0000-0000-7000-8000-000000000021",
                            "resource_type": "product",
                            "operation": "order",
                            "period_unit": "month",
                            "period_count": 1,
                            "quantity": 1,
                            "currency": "KZT",
                            "unit_price_minor": 700000,
                            "subtotal_minor": 700000,
                            "discount_minor": 0,
                            "tax_minor": 0,
                            "total_minor": 700000
                          }
                        ],
                        "total_minor": 700000
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/IdempotencyReplayed"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Модуль pricing отключён либо ресурс/домен восстановления недоступен."
          },
          "409": {
            "$ref": "#/components/responses/Error409",
            "description": "Конфликт Idempotency-Key; для restore также неактивная интеграция домена или блокировка migration_hold."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Нет активного тарифного назначения, опубликованного предложения, цены нужного периода/операции/валюты; ограничения тарифа; неверный customer_uuid; домен вне периода восстановления. PriceUnavailableException оборачивается как validation_failed, а не отдельный error.code=price_unavailable."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Модуль pricing отключён либо ресурс/домен восстановления недоступен.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Idempotency-Key уже занят; либо домен восстановления использует неактивную интеграцию или имеет migration_hold.",
            "recovery": "Для того же запроса дождитесь завершения и повторите неизменённый запрос с тем же ключом; изменённый запрос требует нового ключа после сверки результата."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Нет активного тарифного назначения, опубликованного предложения, цены нужного периода/операции/валюты; ограничения тарифа; неверный customer_uuid; домен вне периода восстановления. PriceUnavailableException оборачивается как validation_failed, а не отдельный error.code=price_unavailable.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CoreCreateQuoteRequest"
              },
              "examples": {
                "registration": {
                  "summary": "Регистрация",
                  "value": {
                    "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                    "items": [
                      {
                        "resource_type": "domain_zone",
                        "resource_uuid": "019a0000-0000-7000-8000-000000000006",
                        "operation": "register",
                        "period_unit": "year",
                        "period_count": 1,
                        "quantity": 1,
                        "currency": "KZT",
                        "payload": {
                          "domain_name": "reseller-example.kz",
                          "external_id": "domain-3042",
                          "contact_uuids": {
                            "owner": "019a0000-0000-7000-8000-000000000012",
                            "admin": "019a0000-0000-7000-8000-000000000012",
                            "tech": "019a0000-0000-7000-8000-000000000012",
                            "billing": "019a0000-0000-7000-8000-000000000012"
                          },
                          "nameservers": [
                            {
                              "hostname": "ns1.example.com"
                            },
                            {
                              "hostname": "ns2.example.com"
                            }
                          ],
                          "purpose": "Сайт организации",
                          "whois_privacy_enabled": false
                        }
                      }
                    ]
                  }
                },
                "renewal": {
                  "summary": "Продление",
                  "value": {
                    "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                    "items": [
                      {
                        "resource_type": "domain_zone",
                        "resource_uuid": "019a0000-0000-7000-8000-000000000006",
                        "operation": "renew",
                        "period_unit": "year",
                        "period_count": 1,
                        "quantity": 1,
                        "currency": "KZT",
                        "payload": {
                          "domain_service_uuid": "019a0000-0000-7000-8000-000000000041"
                        }
                      }
                    ]
                  }
                },
                "restoration": {
                  "summary": "Восстановление",
                  "value": {
                    "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                    "items": [
                      {
                        "resource_type": "domain_zone",
                        "resource_uuid": "019a0000-0000-7000-8000-000000000006",
                        "operation": "restore",
                        "period_unit": "year",
                        "period_count": 1,
                        "quantity": 1,
                        "currency": "KZT",
                        "payload": {
                          "domain_service_uuid": "019a0000-0000-7000-8000-000000000041"
                        }
                      }
                    ]
                  }
                },
                "hosting": {
                  "summary": "Заказ хостинга (только live при исполнении)",
                  "value": {
                    "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                    "items": [
                      {
                        "resource_type": "product",
                        "resource_uuid": "019a0000-0000-7000-8000-000000000004",
                        "operation": "order",
                        "period_unit": "month",
                        "period_count": 1,
                        "quantity": 1,
                        "currency": "KZT",
                        "payload": {
                          "domain_name": "www.example.com",
                          "external_id": "hosting-3043",
                          "notes": "Основной сайт"
                        }
                      }
                    ]
                  }
                }
              }
            }
          }
        }
      }
    },
    "/orders": {
      "get": {
        "operationId": "listOrders",
        "tags": [
          "Заказы"
        ],
        "summary": "Список заказов",
        "description": "Все данные ограничены текущим реселлером и средой ключа test/live. Смена среды параметром запроса не поддерживается. Сортировка: id по убыванию. Пагинация cursor/per_page (по умолчанию 25, фактически ограничивается 1..100). Не поддерживаются page, offset, sort, include и произвольные фильтры. При следующем запросе передавайте исходные фильтры вместе с cursor; ссылки не гарантируют сохранение фильтров.",
        "x-required-scope": "orders.read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          },
          {
            "name": "customer_uuid",
            "in": "query",
            "description": "Фильтр по UUID покупателя.",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "external_id",
            "in": "query",
            "description": "Точное совпадение ID заказа внешнего биллинга.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Точное совпадение статуса; неизвестное значение обычно даёт пустой список.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreOrderPage"
                },
                "examples": {
                  "last_page": {
                    "summary": "Заказы в текущей среде",
                    "value": {
                      "data": [
                        {
                          "uuid": "019a0000-0000-7000-8000-000000000030",
                          "external_id": "order-2042",
                          "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                          "number": "ORD-20261005090000-DEMO42",
                          "status": "submitted",
                          "currency": "KZT",
                          "total_minor": 700000,
                          "submitted_at": "2026-10-05T09:00:00+00:00",
                          "paid_at": null,
                          "completed_at": null,
                          "failed_at": null,
                          "failure_reason": null,
                          "items": [
                            {
                              "uuid": "019a0000-0000-7000-8000-000000000031",
                              "type": "domain",
                              "name": "Домен .kz",
                              "quantity": 1,
                              "unit_price_minor": 700000,
                              "total_minor": 700000,
                              "currency": "KZT",
                              "service_uuid": "019a0000-0000-7000-8000-000000000040",
                              "service_external_id": "domain-3042"
                            }
                          ],
                          "invoice": {
                            "uuid": "019a0000-0000-7000-8000-000000000032",
                            "number": "INV-20261005090000-DEMO42",
                            "status": "issued",
                            "currency": "KZT",
                            "total_minor": 700000,
                            "paid_minor": 0,
                            "payment_collected_by_platform": true
                          },
                          "created_at": "2026-10-05T09:00:00+00:00",
                          "updated_at": "2026-10-05T09:00:00+00:00"
                        },
                        {
                          "uuid": "019a0000-0000-7000-8000-000000000033",
                          "external_id": "order-2043",
                          "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                          "number": "ORD-20261005090000-DEMO43",
                          "status": "registration_failed",
                          "currency": "KZT",
                          "total_minor": 700000,
                          "submitted_at": "2026-10-05T09:00:00+00:00",
                          "paid_at": "2026-10-05T09:00:00+00:00",
                          "completed_at": null,
                          "failed_at": "2026-10-05T09:03:00+00:00",
                          "failure_reason": "Domain registration was rejected by the registrar.",
                          "items": [
                            {
                              "uuid": "019a0000-0000-7000-8000-000000000034",
                              "type": "domain",
                              "name": "Домен .kz",
                              "quantity": 1,
                              "unit_price_minor": 700000,
                              "total_minor": 700000,
                              "currency": "KZT",
                              "service_uuid": "019a0000-0000-7000-8000-000000000044",
                              "service_external_id": "domain-3043"
                            }
                          ],
                          "invoice": {
                            "uuid": "019a0000-0000-7000-8000-000000000035",
                            "number": "INV-20261005090000-DEMO43",
                            "status": "paid",
                            "currency": "KZT",
                            "total_minor": 700000,
                            "paid_minor": 700000,
                            "payment_collected_by_platform": false
                          },
                          "created_at": "2026-10-05T09:00:00+00:00",
                          "updated_at": "2026-10-05T09:03:00+00:00"
                        }
                      ],
                      "links": {
                        "first": null,
                        "last": null,
                        "prev": null,
                        "next": null
                      },
                      "meta": {
                        "path": "https://api.example.com/api/reseller/v1/orders",
                        "per_page": 25,
                        "next_cursor": null,
                        "prev_cursor": null
                      }
                    }
                  },
                  "empty": {
                    "summary": "Нет записей в текущем контексте",
                    "value": {
                      "data": [],
                      "links": {
                        "first": null,
                        "last": null,
                        "prev": null,
                        "next": null
                      },
                      "meta": {
                        "path": "https://api.example.com/api/reseller/v1/orders",
                        "per_page": 25,
                        "next_cursor": null,
                        "prev_cursor": null
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Параметры не прошли валидацию или не выполнены бизнес-условия."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Параметры не прошли валидацию или не выполнены бизнес-условия.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ]
      },
      "post": {
        "operationId": "createOrder",
        "tags": [
          "Заказы"
        ],
        "summary": "Создать заказ по котировке",
        "description": "Исполняет строки order/register котировки. Для доменных строк дополнительно нужен domains.register; для hosting нужен hosting.order. Domain quantity=1, year, 1..10; все четыре контакта должны быть active у того же покупателя и в той же среде. test не поддерживает product-заказы. Для external checkout счёт помечается paid (payment_collected_by_platform=false), исполнение асинхронное; для platform checkout создание заказа не принимает оплату через этот REST API. Итоговые ошибки реестра отслеживайте по заказу/услуге/webhooks. Результат HTTP 201 не гарантирует регистрацию. Все данные ограничены текущим реселлером и средой ключа test/live. Смена среды параметром запроса не поддерживается.",
        "x-required-scope": "orders.create",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "201": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreOrderEnvelope"
                },
                "examples": {
                  "platform_checkout": {
                    "summary": "Счёт ожидает оплаты",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000030",
                        "external_id": "order-2042",
                        "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                        "number": "ORD-20261005090000-DEMO42",
                        "status": "submitted",
                        "currency": "KZT",
                        "total_minor": 700000,
                        "submitted_at": "2026-10-05T09:00:00+00:00",
                        "paid_at": null,
                        "completed_at": null,
                        "failed_at": null,
                        "failure_reason": null,
                        "items": [
                          {
                            "uuid": "019a0000-0000-7000-8000-000000000031",
                            "type": "domain",
                            "name": "Домен .kz",
                            "quantity": 1,
                            "unit_price_minor": 700000,
                            "total_minor": 700000,
                            "currency": "KZT",
                            "service_uuid": "019a0000-0000-7000-8000-000000000040",
                            "service_external_id": "domain-3042"
                          }
                        ],
                        "invoice": {
                          "uuid": "019a0000-0000-7000-8000-000000000032",
                          "number": "INV-20261005090000-DEMO42",
                          "status": "issued",
                          "currency": "KZT",
                          "total_minor": 700000,
                          "paid_minor": 0,
                          "payment_collected_by_platform": true
                        },
                        "created_at": "2026-10-05T09:00:00+00:00",
                        "updated_at": "2026-10-05T09:00:00+00:00"
                      }
                    }
                  },
                  "external_checkout": {
                    "summary": "Оплата принимается реселлером, исполнение ещё впереди",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000030",
                        "external_id": "order-2042",
                        "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                        "number": "ORD-20261005090000-DEMO42",
                        "status": "paid",
                        "currency": "KZT",
                        "total_minor": 700000,
                        "submitted_at": "2026-10-05T09:00:00+00:00",
                        "paid_at": "2026-10-05T09:00:00+00:00",
                        "completed_at": null,
                        "failed_at": null,
                        "failure_reason": null,
                        "items": [
                          {
                            "uuid": "019a0000-0000-7000-8000-000000000031",
                            "type": "domain",
                            "name": "Домен .kz",
                            "quantity": 1,
                            "unit_price_minor": 700000,
                            "total_minor": 700000,
                            "currency": "KZT",
                            "service_uuid": "019a0000-0000-7000-8000-000000000040",
                            "service_external_id": "domain-3042"
                          }
                        ],
                        "invoice": {
                          "uuid": "019a0000-0000-7000-8000-000000000032",
                          "number": "INV-20261005090000-DEMO42",
                          "status": "paid",
                          "currency": "KZT",
                          "total_minor": 700000,
                          "paid_minor": 700000,
                          "payment_collected_by_platform": false
                        },
                        "created_at": "2026-10-05T09:00:00+00:00",
                        "updated_at": "2026-10-05T09:00:00+00:00"
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/IdempotencyReplayed"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Не хватает orders.create, domains.register для доменных строк или hosting.order для hosting-строк."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Котировка не найдена у текущего ключа/покупателя/реселлера."
          },
          "409": {
            "$ref": "#/components/responses/Error409",
            "description": "Idempotency-Key уже обрабатывается либо использован для другого метода, URL/query или тела."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Котировка истекла/использована, другая среда, неподдерживаемая операция; test с product; занятый/дублирующий домен; отсутствующие/чужие контакты; несоответствие зоны; повторный external_id; у покупателя нет владельца для заказа."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Не хватает orders.create, domains.register для доменных строк или hosting.order для hosting-строк.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Котировка не найдена у текущего ключа/покупателя/реселлера.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Idempotency-Key уже обрабатывается либо использован для другого метода, URL/query или тела.",
            "recovery": "Для того же запроса дождитесь завершения и повторите неизменённый запрос с тем же ключом; изменённый запрос требует нового ключа после сверки результата."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Котировка истекла/использована, другая среда, неподдерживаемая операция; test с product; занятый/дублирующий домен; отсутствующие/чужие контакты; несоответствие зоны; повторный external_id; у покупателя нет владельца для заказа.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CoreCreateOrderRequest"
              },
              "examples": {
                "managed": {
                  "summary": "Заказ для управляемого покупателя",
                  "value": {
                    "quote_uuid": "019a0000-0000-7000-8000-000000000020",
                    "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                    "external_id": "order-2042"
                  }
                },
                "aggregate": {
                  "summary": "Для агрегированного покупателя",
                  "value": {
                    "quote_uuid": "019a0000-0000-7000-8000-000000000020",
                    "external_id": "order-2042"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/hosting/orders": {
      "post": {
        "operationId": "createHostingOrder",
        "tags": [
          "Хостинг"
        ],
        "summary": "Создать заказ хостинга",
        "description": "Алиас контроллера /orders с дополнительным scope hosting.order. Сначала получите product/order quote с доменом сайта в payload.domain_name. Только live: test с product даёт 422. Контроллер не ограничивает алиас только hosting-строками; интеграция должна сама выбрать hosting-продукт. HTTP 201 не означает, что Plesk уже создал аккаунт: проверяйте GET /services/{serviceUuid}. Нет reseller REST методов управления панелью, отправки пароля, смены тарифа или продления хостинга. Исполняет строки order/register котировки. Для доменных строк дополнительно нужен domains.register; для hosting нужен hosting.order. Domain quantity=1, year, 1..10; все четыре контакта должны быть active у того же покупателя и в той же среде. test не поддерживает product-заказы. Для external checkout счёт помечается paid (payment_collected_by_platform=false), исполнение асинхронное; для platform checkout создание заказа не принимает оплату через этот REST API. Итоговые ошибки реестра отслеживайте по заказу/услуге/webhooks. Результат HTTP 201 не гарантирует регистрацию. Все данные ограничены текущим реселлером и средой ключа test/live. Смена среды параметром запроса не поддерживается.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "201": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreOrderEnvelope"
                },
                "examples": {
                  "accepted": {
                    "summary": "Заказ хостинга принят",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000030",
                        "external_id": "order-2042",
                        "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                        "number": "ORD-20261005090000-DEMO42",
                        "status": "paid",
                        "currency": "KZT",
                        "total_minor": 700000,
                        "submitted_at": "2026-10-05T09:00:00+00:00",
                        "paid_at": "2026-10-05T09:00:00+00:00",
                        "completed_at": null,
                        "failed_at": null,
                        "failure_reason": null,
                        "items": [
                          {
                            "uuid": "019a0000-0000-7000-8000-000000000031",
                            "type": "hosting",
                            "name": "Hosting Standard",
                            "quantity": 1,
                            "unit_price_minor": 700000,
                            "total_minor": 700000,
                            "currency": "KZT",
                            "service_uuid": "019a0000-0000-7000-8000-000000000042",
                            "service_external_id": "hosting-3043"
                          }
                        ],
                        "invoice": {
                          "uuid": "019a0000-0000-7000-8000-000000000032",
                          "number": "INV-20261005090000-DEMO42",
                          "status": "paid",
                          "currency": "KZT",
                          "total_minor": 700000,
                          "paid_minor": 700000,
                          "payment_collected_by_platform": false
                        },
                        "created_at": "2026-10-05T09:00:00+00:00",
                        "updated_at": "2026-10-05T09:00:00+00:00"
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/IdempotencyReplayed"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Не хватает orders.create, domains.register для доменных строк или hosting.order для hosting-строк."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Котировка не найдена у текущего ключа/покупателя/реселлера."
          },
          "409": {
            "$ref": "#/components/responses/Error409",
            "description": "Idempotency-Key уже обрабатывается либо использован для другого метода, URL/query или тела."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Котировка истекла/использована, другая среда, неподдерживаемая операция; test с product; занятый/дублирующий домен; отсутствующие/чужие контакты; несоответствие зоны; повторный external_id; у покупателя нет владельца для заказа."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Не хватает orders.create, domains.register для доменных строк или hosting.order для hosting-строк.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Котировка не найдена у текущего ключа/покупателя/реселлера.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Idempotency-Key уже обрабатывается либо использован для другого метода, URL/query или тела.",
            "recovery": "Для того же запроса дождитесь завершения и повторите неизменённый запрос с тем же ключом; изменённый запрос требует нового ключа после сверки результата."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Котировка истекла/использована, другая среда, неподдерживаемая операция; test с product; занятый/дублирующий домен; отсутствующие/чужие контакты; несоответствие зоны; повторный external_id; у покупателя нет владельца для заказа.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CoreCreateOrderRequest"
              },
              "examples": {
                "managed": {
                  "summary": "Заказ для управляемого покупателя",
                  "value": {
                    "quote_uuid": "019a0000-0000-7000-8000-000000000020",
                    "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                    "external_id": "order-2042"
                  }
                },
                "aggregate": {
                  "summary": "Для агрегированного покупателя",
                  "value": {
                    "quote_uuid": "019a0000-0000-7000-8000-000000000020",
                    "external_id": "order-2042"
                  }
                }
              }
            }
          }
        },
        "x-required-scopes": [
          "orders.create",
          "hosting.order"
        ]
      }
    },
    "/orders/{orderUuid}": {
      "get": {
        "operationId": "getOrder",
        "tags": [
          "Заказы"
        ],
        "summary": "Получить заказ",
        "description": "Чтение локального состояния без запроса к реестру. В items возвращается service_uuid для дальнейшего GET /services/{serviceUuid}. Отказ реестра может быть отражён асинхронно после HTTP 201. UUID чужого реселлера/среды недоступен. Не трактуйте отсутствие в test как отсутствие в live.",
        "x-required-scope": "orders.read",
        "parameters": [
          {
            "name": "orderUuid",
            "in": "path",
            "required": true,
            "description": "UUID ресурса текущего реселлера.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreOrderEnvelope"
                },
                "examples": {
                  "submitted": {
                    "summary": "Ожидает оплаты",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000030",
                        "external_id": "order-2042",
                        "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                        "number": "ORD-20261005090000-DEMO42",
                        "status": "submitted",
                        "currency": "KZT",
                        "total_minor": 700000,
                        "submitted_at": "2026-10-05T09:00:00+00:00",
                        "paid_at": null,
                        "completed_at": null,
                        "failed_at": null,
                        "failure_reason": null,
                        "items": [
                          {
                            "uuid": "019a0000-0000-7000-8000-000000000031",
                            "type": "domain",
                            "name": "Домен .kz",
                            "quantity": 1,
                            "unit_price_minor": 700000,
                            "total_minor": 700000,
                            "currency": "KZT",
                            "service_uuid": "019a0000-0000-7000-8000-000000000040",
                            "service_external_id": "domain-3042"
                          }
                        ],
                        "invoice": {
                          "uuid": "019a0000-0000-7000-8000-000000000032",
                          "number": "INV-20261005090000-DEMO42",
                          "status": "issued",
                          "currency": "KZT",
                          "total_minor": 700000,
                          "paid_minor": 0,
                          "payment_collected_by_platform": true
                        },
                        "created_at": "2026-10-05T09:00:00+00:00",
                        "updated_at": "2026-10-05T09:00:00+00:00"
                      }
                    }
                  },
                  "paid": {
                    "summary": "Оплачен, ожидает исполнения",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000030",
                        "external_id": "order-2042",
                        "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                        "number": "ORD-20261005090000-DEMO42",
                        "status": "paid",
                        "currency": "KZT",
                        "total_minor": 700000,
                        "submitted_at": "2026-10-05T09:00:00+00:00",
                        "paid_at": "2026-10-05T09:00:00+00:00",
                        "completed_at": null,
                        "failed_at": null,
                        "failure_reason": null,
                        "items": [
                          {
                            "uuid": "019a0000-0000-7000-8000-000000000031",
                            "type": "domain",
                            "name": "Домен .kz",
                            "quantity": 1,
                            "unit_price_minor": 700000,
                            "total_minor": 700000,
                            "currency": "KZT",
                            "service_uuid": "019a0000-0000-7000-8000-000000000040",
                            "service_external_id": "domain-3042"
                          }
                        ],
                        "invoice": {
                          "uuid": "019a0000-0000-7000-8000-000000000032",
                          "number": "INV-20261005090000-DEMO42",
                          "status": "paid",
                          "currency": "KZT",
                          "total_minor": 700000,
                          "paid_minor": 700000,
                          "payment_collected_by_platform": false
                        },
                        "created_at": "2026-10-05T09:00:00+00:00",
                        "updated_at": "2026-10-05T09:00:00+00:00"
                      }
                    }
                  },
                  "registration_failed": {
                    "summary": "Регистрация завершилась отказом",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000030",
                        "external_id": "order-2042",
                        "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                        "number": "ORD-20261005090000-DEMO42",
                        "status": "registration_failed",
                        "currency": "KZT",
                        "total_minor": 700000,
                        "submitted_at": "2026-10-05T09:00:00+00:00",
                        "paid_at": "2026-10-05T09:00:00+00:00",
                        "completed_at": null,
                        "failed_at": "2026-10-05T09:03:00+00:00",
                        "failure_reason": "Domain registration was rejected by the registrar.",
                        "items": [
                          {
                            "uuid": "019a0000-0000-7000-8000-000000000031",
                            "type": "domain",
                            "name": "Домен .kz",
                            "quantity": 1,
                            "unit_price_minor": 700000,
                            "total_minor": 700000,
                            "currency": "KZT",
                            "service_uuid": "019a0000-0000-7000-8000-000000000040",
                            "service_external_id": "domain-3042"
                          }
                        ],
                        "invoice": {
                          "uuid": "019a0000-0000-7000-8000-000000000032",
                          "number": "INV-20261005090000-DEMO42",
                          "status": "paid",
                          "currency": "KZT",
                          "total_minor": 700000,
                          "paid_minor": 700000,
                          "payment_collected_by_platform": false
                        },
                        "created_at": "2026-10-05T09:00:00+00:00",
                        "updated_at": "2026-10-05T09:03:00+00:00"
                      }
                    }
                  },
                  "completed": {
                    "summary": "Заказ выполнен",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000030",
                        "external_id": "order-2042",
                        "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                        "number": "ORD-20261005090000-DEMO42",
                        "status": "completed",
                        "currency": "KZT",
                        "total_minor": 700000,
                        "submitted_at": "2026-10-05T09:00:00+00:00",
                        "paid_at": "2026-10-05T09:00:00+00:00",
                        "completed_at": "2026-10-05T09:03:00+00:00",
                        "failed_at": null,
                        "failure_reason": null,
                        "items": [
                          {
                            "uuid": "019a0000-0000-7000-8000-000000000031",
                            "type": "domain",
                            "name": "Домен .kz",
                            "quantity": 1,
                            "unit_price_minor": 700000,
                            "total_minor": 700000,
                            "currency": "KZT",
                            "service_uuid": "019a0000-0000-7000-8000-000000000040",
                            "service_external_id": "domain-3042"
                          }
                        ],
                        "invoice": {
                          "uuid": "019a0000-0000-7000-8000-000000000032",
                          "number": "INV-20261005090000-DEMO42",
                          "status": "paid",
                          "currency": "KZT",
                          "total_minor": 700000,
                          "paid_minor": 700000,
                          "payment_collected_by_platform": false
                        },
                        "created_at": "2026-10-05T09:00:00+00:00",
                        "updated_at": "2026-10-05T09:03:00+00:00"
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Параметры не прошли валидацию или не выполнены бизнес-условия."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Параметры не прошли валидацию или не выполнены бизнес-условия.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ]
      }
    },
    "/services": {
      "get": {
        "operationId": "listServices",
        "tags": [
          "Услуги"
        ],
        "summary": "Список услуг",
        "description": "Все данные ограничены текущим реселлером и средой ключа test/live. Смена среды параметром запроса не поддерживается. Сортировка id по убыванию. Пагинация cursor/per_page (по умолчанию 25, фактически ограничивается 1..100). Не поддерживаются page, offset, sort, include и произвольные фильтры. При следующем запросе передавайте исходные фильтры вместе с cursor; ссылки не гарантируют сохранение фильтров.",
        "x-required-scope": "services.read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          },
          {
            "name": "customer_uuid",
            "in": "query",
            "description": "Точный UUID покупателя.",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "external_id",
            "in": "query",
            "description": "Точный ID услуги внешнего биллинга.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "Тип, например domain или hosting.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Точное состояние услуги.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreServicePage"
                },
                "examples": {
                  "last_page": {
                    "summary": "Доменная и хостинговая услуги",
                    "value": {
                      "data": [
                        {
                          "uuid": "019a0000-0000-7000-8000-000000000040",
                          "external_id": "domain-3042",
                          "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                          "type": "domain",
                          "status": "active",
                          "name": "reseller-example.kz",
                          "starts_at": "2026-10-05T09:00:00+00:00",
                          "expires_at": "2027-10-05T09:00:00+00:00",
                          "auto_renew": true,
                          "domain": {
                            "uuid": "019a0000-0000-7000-8000-000000000041",
                            "external_id": "domain-3042",
                            "domain_name": "reseller-example.kz",
                            "status": "active",
                            "registration_status": "registered",
                            "verification_status": "not_required",
                            "registry_status": "ok"
                          },
                          "hosting": null,
                          "created_at": "2026-10-05T09:00:00+00:00",
                          "updated_at": "2026-10-05T09:00:00+00:00"
                        },
                        {
                          "uuid": "019a0000-0000-7000-8000-000000000042",
                          "external_id": "hosting-3043",
                          "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                          "type": "hosting",
                          "status": "active",
                          "name": "www.example.com",
                          "starts_at": "2026-10-05T09:00:00+00:00",
                          "expires_at": "2026-11-05T09:00:00+00:00",
                          "auto_renew": true,
                          "domain": null,
                          "hosting": {
                            "uuid": "019a0000-0000-7000-8000-000000000043",
                            "domain_name": "www.example.com",
                            "status": "active",
                            "plan_code": "hosting-standard",
                            "provisioned_at": "2026-10-05T09:00:00+00:00"
                          },
                          "created_at": "2026-10-05T09:00:00+00:00",
                          "updated_at": "2026-10-05T09:00:00+00:00"
                        }
                      ],
                      "links": {
                        "first": null,
                        "last": null,
                        "prev": null,
                        "next": null
                      },
                      "meta": {
                        "path": "https://api.example.com/api/reseller/v1/services",
                        "per_page": 25,
                        "next_cursor": null,
                        "prev_cursor": null
                      }
                    }
                  },
                  "empty": {
                    "summary": "Нет записей в текущем контексте",
                    "value": {
                      "data": [],
                      "links": {
                        "first": null,
                        "last": null,
                        "prev": null,
                        "next": null
                      },
                      "meta": {
                        "path": "https://api.example.com/api/reseller/v1/services",
                        "per_page": 25,
                        "next_cursor": null,
                        "prev_cursor": null
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Параметры не прошли валидацию или не выполнены бизнес-условия."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Параметры не прошли валидацию или не выполнены бизнес-условия.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ]
      }
    },
    "/services/{serviceUuid}": {
      "get": {
        "operationId": "getService",
        "tags": [
          "Услуги"
        ],
        "summary": "Получить услугу",
        "description": "Общая карточка услуги с краткими domain/hosting. serviceUuid и domain.uuid/hosting.uuid различаются. Пароли и доступы к панели не выдаются. Общий auto_renew не заменяет отдельное согласие на API-автопродление домена. UUID чужого реселлера/среды недоступен. Не трактуйте отсутствие в test как отсутствие в live.",
        "x-required-scope": "services.read",
        "parameters": [
          {
            "name": "serviceUuid",
            "in": "path",
            "required": true,
            "description": "UUID ресурса текущего реселлера.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreServiceEnvelope"
                },
                "examples": {
                  "active_domain": {
                    "summary": "Активный домен",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000040",
                        "external_id": "domain-3042",
                        "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                        "type": "domain",
                        "status": "active",
                        "name": "reseller-example.kz",
                        "starts_at": "2026-10-05T09:00:00+00:00",
                        "expires_at": "2027-10-05T09:00:00+00:00",
                        "auto_renew": true,
                        "domain": {
                          "uuid": "019a0000-0000-7000-8000-000000000041",
                          "external_id": "domain-3042",
                          "domain_name": "reseller-example.kz",
                          "status": "active",
                          "registration_status": "registered",
                          "verification_status": "not_required",
                          "registry_status": "ok"
                        },
                        "hosting": null,
                        "created_at": "2026-10-05T09:00:00+00:00",
                        "updated_at": "2026-10-05T09:00:00+00:00"
                      }
                    }
                  },
                  "pending": {
                    "summary": "Создан заказ, доменный объект ещё отсутствует",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000040",
                        "external_id": "domain-3042",
                        "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                        "type": "domain",
                        "status": "pending",
                        "name": "reseller-example.kz",
                        "starts_at": "2026-10-05T09:00:00+00:00",
                        "expires_at": "2027-10-05T09:00:00+00:00",
                        "auto_renew": true,
                        "domain": null,
                        "hosting": null,
                        "created_at": "2026-10-05T09:00:00+00:00",
                        "updated_at": "2026-10-05T09:00:00+00:00"
                      }
                    }
                  },
                  "hosting": {
                    "summary": "Активный хостинг",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000042",
                        "external_id": "hosting-3043",
                        "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                        "type": "hosting",
                        "status": "active",
                        "name": "www.example.com",
                        "starts_at": "2026-10-05T09:00:00+00:00",
                        "expires_at": "2026-11-05T09:00:00+00:00",
                        "auto_renew": true,
                        "domain": null,
                        "hosting": {
                          "uuid": "019a0000-0000-7000-8000-000000000043",
                          "domain_name": "www.example.com",
                          "status": "active",
                          "plan_code": "hosting-standard",
                          "provisioned_at": "2026-10-05T09:00:00+00:00"
                        },
                        "created_at": "2026-10-05T09:00:00+00:00",
                        "updated_at": "2026-10-05T09:00:00+00:00"
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Параметры не прошли валидацию или не выполнены бизнес-условия."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Параметры не прошли валидацию или не выполнены бизнес-условия.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ]
      }
    },
    "/customers": {
      "get": {
        "operationId": "listCustomers",
        "tags": [
          "Покупатели"
        ],
        "summary": "Список покупателей",
        "description": "Все данные ограничены текущим реселлером и средой ключа test/live. Смена среды параметром запроса не поддерживается. Системные/агрегированные customer metadata.system=true исключены из списка. Сортировка id по возрастанию. Пагинация cursor/per_page (по умолчанию 25, фактически ограничивается 1..100). Не поддерживаются page, offset, sort, include и произвольные фильтры. При следующем запросе передавайте исходные фильтры вместе с cursor; ссылки не гарантируют сохранение фильтров.",
        "x-required-scope": "customers.read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          },
          {
            "name": "external_id",
            "in": "query",
            "description": "Точный ID покупателя внешнего биллинга.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreCustomerPage"
                },
                "examples": {
                  "last_page": {
                    "summary": "Физическое и юридическое лицо",
                    "value": {
                      "data": [
                        {
                          "uuid": "019a0000-0000-7000-8000-000000000010",
                          "external_id": "customer-1042",
                          "type": "individual",
                          "status": "active",
                          "display_name": "Иван Петров",
                          "email": "ivan@example.com",
                          "phone": "+77010000000",
                          "preferred_currency": "KZT",
                          "locale": "ru",
                          "timezone": "Asia/Almaty",
                          "profile": {
                            "first_name": "Иван",
                            "last_name": "Петров",
                            "middle_name": "Иванович",
                            "birth_date": "1990-01-01",
                            "country_code": "KZ",
                            "city": "Алматы",
                            "address_line": "ул. Примерная, 10",
                            "postal_code": "050000"
                          },
                          "created_at": "2026-10-05T09:00:00+00:00",
                          "updated_at": "2026-10-05T09:00:00+00:00"
                        },
                        {
                          "uuid": "019a0000-0000-7000-8000-000000000011",
                          "external_id": "company-1043",
                          "type": "legal",
                          "status": "active",
                          "display_name": "ТОО \"Example\"",
                          "email": "admin@example.com",
                          "phone": "+77010000000",
                          "preferred_currency": "KZT",
                          "locale": "ru",
                          "timezone": "Asia/Almaty",
                          "profile": {
                            "company_name": "ТОО \"Example\"",
                            "registration_number": "EXAMPLE-2026",
                            "tax_number": null,
                            "legal_address": "Алматы, ул. Примерная, 10",
                            "actual_address": "Алматы, ул. Примерная, 10",
                            "director_full_name": "Иван Петров"
                          },
                          "created_at": "2026-10-05T09:00:00+00:00",
                          "updated_at": "2026-10-05T09:00:00+00:00"
                        }
                      ],
                      "meta": {
                        "next_cursor": null,
                        "per_page": 25
                      }
                    }
                  },
                  "empty": {
                    "summary": "Нет записей в текущем контексте",
                    "value": {
                      "data": [],
                      "meta": {
                        "next_cursor": null,
                        "per_page": 25
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Параметры не прошли валидацию или не выполнены бизнес-условия."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Параметры не прошли валидацию или не выполнены бизнес-условия.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ]
      },
      "post": {
        "operationId": "createCustomer",
        "tags": [
          "Покупатели"
        ],
        "summary": "Создать покупателя",
        "description": "Только customer_mode=managed. Создаётся покупатель и профиль; live может связать его с существующей пользовательской записью по email. test использует изолированную неактивную пользовательскую запись. Пароль и токен клиентского входа не выдаются. Это не создаёт контакт реестра: отдельно POST /contacts. status из запроса не меняет начальный active.",
        "x-required-scope": "customers.write",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "201": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreCustomerEnvelope"
                },
                "examples": {
                  "individual": {
                    "summary": "Физическое лицо",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000010",
                        "external_id": "customer-1042",
                        "type": "individual",
                        "status": "active",
                        "display_name": "Иван Петров",
                        "email": "ivan@example.com",
                        "phone": "+77010000000",
                        "preferred_currency": "KZT",
                        "locale": "ru",
                        "timezone": "Asia/Almaty",
                        "profile": {
                          "first_name": "Иван",
                          "last_name": "Петров",
                          "middle_name": "Иванович",
                          "birth_date": "1990-01-01",
                          "country_code": "KZ",
                          "city": "Алматы",
                          "address_line": "ул. Примерная, 10",
                          "postal_code": "050000"
                        },
                        "created_at": "2026-10-05T09:00:00+00:00",
                        "updated_at": "2026-10-05T09:00:00+00:00"
                      }
                    }
                  },
                  "legal": {
                    "summary": "Юридическое лицо",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000011",
                        "external_id": "company-1043",
                        "type": "legal",
                        "status": "active",
                        "display_name": "ТОО \"Example\"",
                        "email": "admin@example.com",
                        "phone": "+77010000000",
                        "preferred_currency": "KZT",
                        "locale": "ru",
                        "timezone": "Asia/Almaty",
                        "profile": {
                          "company_name": "ТОО \"Example\"",
                          "registration_number": "EXAMPLE-2026",
                          "tax_number": null,
                          "legal_address": "Алматы, ул. Примерная, 10",
                          "actual_address": "Алматы, ул. Примерная, 10",
                          "director_full_name": "Иван Петров"
                        },
                        "created_at": "2026-10-05T09:00:00+00:00",
                        "updated_at": "2026-10-05T09:00:00+00:00"
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/IdempotencyReplayed"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Режим aggregate не разрешает создание покупателей либо нет customers.write."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте."
          },
          "409": {
            "$ref": "#/components/responses/Error409",
            "description": "Idempotency-Key уже обрабатывается либо использован для другого метода, URL/query или тела."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Неверный email/тип/дата/язык/часовой пояс; повторный external_id внутри реселлера+среды."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Режим aggregate не разрешает создание покупателей либо нет customers.write.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Idempotency-Key уже обрабатывается либо использован для другого метода, URL/query или тела.",
            "recovery": "Для того же запроса дождитесь завершения и повторите неизменённый запрос с тем же ключом; изменённый запрос требует нового ключа после сверки результата."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Неверный email/тип/дата/язык/часовой пояс; повторный external_id внутри реселлера+среды.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CoreCreateCustomerRequest"
              },
              "examples": {
                "individual": {
                  "summary": "Физическое лицо",
                  "value": {
                    "external_id": "customer-1042",
                    "type": "individual",
                    "display_name": "Иван Петров",
                    "email": "ivan@example.com",
                    "phone": "+77010000000",
                    "preferred_currency": "KZT",
                    "locale": "ru",
                    "timezone": "Asia/Almaty",
                    "first_name": "Иван",
                    "last_name": "Петров",
                    "middle_name": "Иванович",
                    "birth_date": "1990-01-01",
                    "country_code": "KZ",
                    "city": "Алматы",
                    "address_line": "ул. Примерная, 10",
                    "postal_code": "050000"
                  }
                },
                "legal": {
                  "summary": "Юридическое лицо",
                  "value": {
                    "external_id": "company-1043",
                    "type": "legal",
                    "display_name": "ТОО \"Example\"",
                    "email": "admin@example.com",
                    "phone": "+77010000000",
                    "preferred_currency": "KZT",
                    "locale": "ru",
                    "timezone": "Asia/Almaty",
                    "company_name": "ТОО \"Example\"",
                    "registration_number": "EXAMPLE-2026",
                    "tax_number": null,
                    "legal_address": "Алматы, ул. Примерная, 10",
                    "actual_address": "Алматы, ул. Примерная, 10",
                    "director_full_name": "Иван Петров"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/customers/{customerUuid}": {
      "get": {
        "operationId": "getCustomer",
        "tags": [
          "Покупатели"
        ],
        "summary": "Получить покупателя",
        "description": "UUID чужого реселлера/среды недоступен. Не трактуйте отсутствие в test как отсутствие в live. Системные покупатели исключены.",
        "x-required-scope": "customers.read",
        "parameters": [
          {
            "name": "customerUuid",
            "in": "path",
            "required": true,
            "description": "UUID ресурса текущего реселлера.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreCustomerEnvelope"
                },
                "examples": {
                  "individual": {
                    "summary": "Физическое лицо",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000010",
                        "external_id": "customer-1042",
                        "type": "individual",
                        "status": "active",
                        "display_name": "Иван Петров",
                        "email": "ivan@example.com",
                        "phone": "+77010000000",
                        "preferred_currency": "KZT",
                        "locale": "ru",
                        "timezone": "Asia/Almaty",
                        "profile": {
                          "first_name": "Иван",
                          "last_name": "Петров",
                          "middle_name": "Иванович",
                          "birth_date": "1990-01-01",
                          "country_code": "KZ",
                          "city": "Алматы",
                          "address_line": "ул. Примерная, 10",
                          "postal_code": "050000"
                        },
                        "created_at": "2026-10-05T09:00:00+00:00",
                        "updated_at": "2026-10-05T09:00:00+00:00"
                      }
                    }
                  },
                  "legal": {
                    "summary": "Организация",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000011",
                        "external_id": "company-1043",
                        "type": "legal",
                        "status": "active",
                        "display_name": "ТОО \"Example\"",
                        "email": "admin@example.com",
                        "phone": "+77010000000",
                        "preferred_currency": "KZT",
                        "locale": "ru",
                        "timezone": "Asia/Almaty",
                        "profile": {
                          "company_name": "ТОО \"Example\"",
                          "registration_number": "EXAMPLE-2026",
                          "tax_number": null,
                          "legal_address": "Алматы, ул. Примерная, 10",
                          "actual_address": "Алматы, ул. Примерная, 10",
                          "director_full_name": "Иван Петров"
                        },
                        "created_at": "2026-10-05T09:00:00+00:00",
                        "updated_at": "2026-10-05T09:00:00+00:00"
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Параметры не прошли валидацию или не выполнены бизнес-условия."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Параметры не прошли валидацию или не выполнены бизнес-условия.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ]
      },
      "patch": {
        "operationId": "updateCustomer",
        "tags": [
          "Покупатели"
        ],
        "summary": "Обновить покупателя",
        "description": "Частичный PATCH верхнего уровня. type/email/preferred_currency валидируются, но не изменяются текущим контроллером. null не очищает телефон, external_id или поля профиля. company_name у legal при отсутствии заменяется display_name; передавайте прежнее company_name, если его нужно сохранить. Изменение status не является документированной командой приостановки всех услуг.",
        "x-required-scope": "customers.write",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "customerUuid",
            "in": "path",
            "required": true,
            "description": "UUID ресурса текущего реселлера.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreCustomerEnvelope"
                },
                "examples": {
                  "updated": {
                    "summary": "Изменён телефон",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000010",
                        "external_id": "customer-1042",
                        "type": "individual",
                        "status": "active",
                        "display_name": "Иван Петров",
                        "email": "ivan@example.com",
                        "phone": "+77010000001",
                        "preferred_currency": "KZT",
                        "locale": "ru",
                        "timezone": "Asia/Almaty",
                        "profile": {
                          "first_name": "Иван",
                          "last_name": "Петров",
                          "middle_name": "Иванович",
                          "birth_date": "1990-01-01",
                          "country_code": "KZ",
                          "city": "Алматы",
                          "address_line": "ул. Примерная, 10",
                          "postal_code": "050000"
                        },
                        "created_at": "2026-10-05T09:00:00+00:00",
                        "updated_at": "2026-10-05T09:00:00+00:00"
                      }
                    }
                  },
                  "blocked": {
                    "summary": "Покупатель отмечен blocked",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000010",
                        "external_id": "customer-1042",
                        "type": "individual",
                        "status": "blocked",
                        "display_name": "Иван Петров",
                        "email": "ivan@example.com",
                        "phone": "+77010000000",
                        "preferred_currency": "KZT",
                        "locale": "ru",
                        "timezone": "Asia/Almaty",
                        "profile": {
                          "first_name": "Иван",
                          "last_name": "Петров",
                          "middle_name": "Иванович",
                          "birth_date": "1990-01-01",
                          "country_code": "KZ",
                          "city": "Алматы",
                          "address_line": "ул. Примерная, 10",
                          "postal_code": "050000"
                        },
                        "created_at": "2026-10-05T09:00:00+00:00",
                        "updated_at": "2026-10-05T09:00:00+00:00"
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/IdempotencyReplayed"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте."
          },
          "409": {
            "$ref": "#/components/responses/Error409",
            "description": "Idempotency-Key уже обрабатывается либо использован для другого метода, URL/query или тела."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Неверные значения или занятый external_id. Поля профиля должны находиться на верхнем уровне."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Idempotency-Key уже обрабатывается либо использован для другого метода, URL/query или тела.",
            "recovery": "Для того же запроса дождитесь завершения и повторите неизменённый запрос с тем же ключом; изменённый запрос требует нового ключа после сверки результата."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Неверные значения или занятый external_id. Поля профиля должны находиться на верхнем уровне.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CoreUpdateCustomerRequest"
              },
              "examples": {
                "phone": {
                  "summary": "Изменить телефон физлица",
                  "value": {
                    "phone": "+77010000001"
                  }
                },
                "legal": {
                  "summary": "Изменить адрес с сохранением названия",
                  "value": {
                    "company_name": "ТОО \"Example\"",
                    "legal_address": "Алматы, ул. Примерная, 20"
                  }
                },
                "block": {
                  "summary": "Изменить состояние покупателя",
                  "value": {
                    "status": "blocked"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/contacts": {
      "get": {
        "operationId": "listContacts",
        "tags": [
          "Контакты"
        ],
        "summary": "Контакты покупателя",
        "description": "Возвращает локальные контакты выбранного покупателя, сортировка id по возрастанию. В managed customer_uuid обязателен, отсутствие даёт 422. Фильтр external_id не реализован. Значение документа не возвращается. Все данные ограничены текущим реселлером и средой ключа test/live. Смена среды параметром запроса не поддерживается. Пагинация cursor/per_page (по умолчанию 25, фактически ограничивается 1..100). Не поддерживаются page, offset, sort, include и произвольные фильтры. При следующем запросе передавайте исходные фильтры вместе с cursor; ссылки не гарантируют сохранение фильтров.",
        "x-required-scope": "contacts.read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          },
          {
            "name": "customer_uuid",
            "in": "query",
            "required": false,
            "description": "Обязателен в managed; в aggregate отсутствие выбирает агрегированного покупателя. Неверный/чужой customer_uuid даёт 422, не пустой список.",
            "schema": {
              "type": [
                "string",
                "null"
              ],
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreContactPage"
                },
                "examples": {
                  "last_page": {
                    "summary": "Контакты одного покупателя",
                    "value": {
                      "data": [
                        {
                          "uuid": "019a0000-0000-7000-8000-000000000012",
                          "type": "person",
                          "status": "active",
                          "first_name": "Иван",
                          "last_name": "Петров",
                          "middle_name": "Иванович",
                          "organization_name": null,
                          "email": "ivan@example.com",
                          "phone": "+77010000000",
                          "country_code": "KZ",
                          "residence_country_code": "KZ",
                          "city": "Алматы",
                          "region": "Алматы",
                          "address_line": "ул. Примерная, 10",
                          "postal_code": "050000",
                          "external_id_type": null,
                          "verification_status": "not_required",
                          "is_locked_as_registrant": false,
                          "verified_at": null,
                          "external_id": "contact-1042",
                          "customer_uuid": "019a0000-0000-7000-8000-000000000010"
                        }
                      ],
                      "meta": {
                        "next_cursor": null,
                        "per_page": 25
                      }
                    }
                  },
                  "empty": {
                    "summary": "Нет записей в текущем контексте",
                    "value": {
                      "data": [],
                      "meta": {
                        "next_cursor": null,
                        "per_page": 25
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "customer_uuid отсутствует в managed, имеет неверный формат либо не найден у реселлера в данной среде."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "customer_uuid отсутствует в managed, имеет неверный формат либо не найден у реселлера в данной среде.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ]
      },
      "post": {
        "operationId": "createContact",
        "tags": [
          "Контакты"
        ],
        "summary": "Создать контакт для домена",
        "description": "Создаёт локальную карточку. Контакт реестра создаётся/связывается при доменных операциях, не этой командой. Для person обязательно отчество. residence_country_code и postal_code обязательны несмотря на необязательность в старых примерах. Правила документа зависят от страны резидентства, не от среды ключа.",
        "x-required-scope": "contacts.write",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "201": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreContactEnvelope"
                },
                "examples": {
                  "person": {
                    "summary": "Физическое лицо без документа; для регистрации документ может понадобиться",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000012",
                        "type": "person",
                        "status": "active",
                        "first_name": "Иван",
                        "last_name": "Петров",
                        "middle_name": "Иванович",
                        "organization_name": null,
                        "email": "ivan@example.com",
                        "phone": "+77010000000",
                        "country_code": "KZ",
                        "residence_country_code": "KZ",
                        "city": "Алматы",
                        "region": "Алматы",
                        "address_line": "ул. Примерная, 10",
                        "postal_code": "050000",
                        "external_id_type": null,
                        "verification_status": "not_required",
                        "is_locked_as_registrant": false,
                        "verified_at": null,
                        "external_id": "contact-1042",
                        "customer_uuid": "019a0000-0000-7000-8000-000000000010"
                      }
                    }
                  },
                  "organization": {
                    "summary": "Организация",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000013",
                        "type": "organization",
                        "status": "active",
                        "first_name": null,
                        "last_name": null,
                        "middle_name": null,
                        "organization_name": "ТОО \"Example\"",
                        "email": "admin@example.com",
                        "phone": "+77010000000",
                        "country_code": "KZ",
                        "residence_country_code": "KZ",
                        "city": "Алматы",
                        "region": "Алматы",
                        "address_line": "ул. Примерная, 10",
                        "postal_code": "050000",
                        "external_id_type": null,
                        "verification_status": "not_required",
                        "is_locked_as_registrant": false,
                        "verified_at": null,
                        "external_id": "contact-1043",
                        "customer_uuid": "019a0000-0000-7000-8000-000000000011"
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/IdempotencyReplayed"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте."
          },
          "409": {
            "$ref": "#/components/responses/Error409",
            "description": "Idempotency-Key уже обрабатывается либо использован для другого метода, URL/query или тела."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Отсутствуют обязательные поля (включая middle_name для person), неверный документ/контрольная сумма, customer_uuid не разрешён, external_id уже занят."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Idempotency-Key уже обрабатывается либо использован для другого метода, URL/query или тела.",
            "recovery": "Для того же запроса дождитесь завершения и повторите неизменённый запрос с тем же ключом; изменённый запрос требует нового ключа после сверки результата."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Отсутствуют обязательные поля (включая middle_name для person), неверный документ/контрольная сумма, customer_uuid не разрешён, external_id уже занят.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CoreUpsertContactRequest"
              },
              "examples": {
                "person": {
                  "summary": "Полный контакт физического лица",
                  "value": {
                    "type": "person",
                    "first_name": "Иван",
                    "last_name": "Петров",
                    "middle_name": "Иванович",
                    "organization_name": null,
                    "email": "ivan@example.com",
                    "phone": "+77010000000",
                    "country_code": "KZ",
                    "residence_country_code": "KZ",
                    "city": "Алматы",
                    "region": "Алматы",
                    "address_line": "ул. Примерная, 10",
                    "postal_code": "050000",
                    "external_id": "contact-1042",
                    "customer_uuid": "019a0000-0000-7000-8000-000000000010"
                  }
                },
                "person_with_identity": {
                  "summary": "Контакт с ИИН; синтетический пример с корректной контрольной суммой, замените данными владельца",
                  "value": {
                    "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                    "external_id": "contact-1044",
                    "type": "person",
                    "first_name": "Иван",
                    "last_name": "Петров",
                    "middle_name": "Иванович",
                    "email": "ivan@example.com",
                    "phone": "+77010000000",
                    "country_code": "KZ",
                    "residence_country_code": "KZ",
                    "city": "Алматы",
                    "address_line": "ул. Примерная, 10",
                    "postal_code": "050000",
                    "external_id_type": "IIN",
                    "external_id_value": "900101300116"
                  }
                },
                "organization": {
                  "summary": "Полный контакт организации",
                  "value": {
                    "type": "organization",
                    "first_name": null,
                    "last_name": null,
                    "middle_name": null,
                    "organization_name": "ТОО \"Example\"",
                    "email": "admin@example.com",
                    "phone": "+77010000000",
                    "country_code": "KZ",
                    "residence_country_code": "KZ",
                    "city": "Алматы",
                    "region": "Алматы",
                    "address_line": "ул. Примерная, 10",
                    "postal_code": "050000",
                    "external_id": "contact-1043",
                    "customer_uuid": "019a0000-0000-7000-8000-000000000011"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/contacts/{contactUuid}": {
      "get": {
        "operationId": "getContact",
        "tags": [
          "Контакты"
        ],
        "summary": "Получить контакт",
        "description": "В managed customer_uuid нужен даже при известном contactUuid. external_id_value не возвращается. UUID чужого реселлера/среды недоступен. Не трактуйте отсутствие в test как отсутствие в live.",
        "x-required-scope": "contacts.read",
        "parameters": [
          {
            "name": "contactUuid",
            "in": "path",
            "required": true,
            "description": "UUID ресурса текущего реселлера.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "customer_uuid",
            "in": "query",
            "required": false,
            "description": "Обязателен в managed; в aggregate отсутствие выбирает агрегированного покупателя. Неверный/чужой customer_uuid даёт 422, не пустой список.",
            "schema": {
              "type": [
                "string",
                "null"
              ],
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreContactEnvelope"
                },
                "examples": {
                  "person": {
                    "summary": "Локальная карточка",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000012",
                        "type": "person",
                        "status": "active",
                        "first_name": "Иван",
                        "last_name": "Петров",
                        "middle_name": "Иванович",
                        "organization_name": null,
                        "email": "ivan@example.com",
                        "phone": "+77010000000",
                        "country_code": "KZ",
                        "residence_country_code": "KZ",
                        "city": "Алматы",
                        "region": "Алматы",
                        "address_line": "ул. Примерная, 10",
                        "postal_code": "050000",
                        "external_id_type": null,
                        "verification_status": "not_required",
                        "is_locked_as_registrant": false,
                        "verified_at": null,
                        "external_id": "contact-1042",
                        "customer_uuid": "019a0000-0000-7000-8000-000000000010"
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Контакт не принадлежит выбранному покупателю или среде."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Неверный/отсутствующий customer_uuid согласно режиму."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Контакт не принадлежит выбранному покупателю или среде.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Неверный/отсутствующий customer_uuid согласно режиму.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ]
      },
      "patch": {
        "operationId": "updateContact",
        "tags": [
          "Контакты"
        ],
        "summary": "Заменить данные контакта",
        "description": "PATCH требует полную карточку по правилам POST: type, email, phone, страны, город, адрес, индекс и условные ФИО/организация. customer_uuid передавайте в JSON. Отсутствующие необязательные поля карточки обнуляются; external_id сохраняется, если не передан. Документ сохраняется при отсутствии обоих полей документа. Эта операция меняет локальную карточку, не выполняет EPP update; для домена используйте PUT /domains/{uuid}/contacts.",
        "x-required-scope": "contacts.write",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "contactUuid",
            "in": "path",
            "required": true,
            "description": "UUID ресурса текущего реселлера.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreContactEnvelope"
                },
                "examples": {
                  "updated": {
                    "summary": "Контакт с новым адресом",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000012",
                        "type": "person",
                        "status": "active",
                        "first_name": "Иван",
                        "last_name": "Петров",
                        "middle_name": "Иванович",
                        "organization_name": null,
                        "email": "ivan@example.com",
                        "phone": "+77010000000",
                        "country_code": "KZ",
                        "residence_country_code": "KZ",
                        "city": "Алматы",
                        "region": "Алматы",
                        "address_line": "ул. Примерная, 20",
                        "postal_code": "050000",
                        "external_id_type": null,
                        "verification_status": "not_required",
                        "is_locked_as_registrant": false,
                        "verified_at": null,
                        "external_id": "contact-1042",
                        "customer_uuid": "019a0000-0000-7000-8000-000000000010"
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/IdempotencyReplayed"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Контакт не найден у выбранного покупателя/среды."
          },
          "409": {
            "$ref": "#/components/responses/Error409",
            "description": "Idempotency-Key уже обрабатывается либо использован для другого метода, URL/query или тела."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Передан частичный набор обязательных полей, невалидный документ либо занятый external_id."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Контакт не найден у выбранного покупателя/среды.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Idempotency-Key уже обрабатывается либо использован для другого метода, URL/query или тела.",
            "recovery": "Для того же запроса дождитесь завершения и повторите неизменённый запрос с тем же ключом; изменённый запрос требует нового ключа после сверки результата."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Передан частичный набор обязательных полей, невалидный документ либо занятый external_id.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CoreUpsertContactRequest"
              },
              "examples": {
                "full": {
                  "summary": "Полная замена с новым адресом",
                  "value": {
                    "type": "person",
                    "first_name": "Иван",
                    "last_name": "Петров",
                    "middle_name": "Иванович",
                    "organization_name": null,
                    "email": "ivan@example.com",
                    "phone": "+77010000000",
                    "country_code": "KZ",
                    "residence_country_code": "KZ",
                    "city": "Алматы",
                    "region": "Алматы",
                    "address_line": "ул. Примерная, 20",
                    "postal_code": "050000",
                    "external_id": "contact-1042",
                    "customer_uuid": "019a0000-0000-7000-8000-000000000010"
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteContact",
        "tags": [
          "Контакты"
        ],
        "summary": "Удалить неиспользуемый контакт",
        "description": "Удаление запрещено, если контакт используется в owner/admin/tech/billing существующего домена или ожидающей регистрации. Не удаляет домены и не выполняет удаление EPP-контакта. customer_uuid передавайте query-параметром. Все данные ограничены текущим реселлером и средой ключа test/live. Смена среды параметром запроса не поддерживается.",
        "x-required-scope": "contacts.write",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "contactUuid",
            "in": "path",
            "required": true,
            "description": "UUID ресурса текущего реселлера.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "customer_uuid",
            "in": "query",
            "required": false,
            "description": "Обязателен в managed; в aggregate отсутствие выбирает агрегированного покупателя. Неверный/чужой customer_uuid даёт 422, не пустой список.",
            "schema": {
              "type": [
                "string",
                "null"
              ],
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreContactDeletedEnvelope"
                },
                "examples": {
                  "deleted": {
                    "summary": "Локальный контакт удалён",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000012",
                        "deleted": true
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/IdempotencyReplayed"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте."
          },
          "409": {
            "$ref": "#/components/responses/Error409",
            "description": "Контакт используется существующим доменом или pending/provisioning регистрацией; также возможен конфликт Idempotency-Key."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "customer_uuid отсутствует в managed или не разрешён."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Scope, IP allowlist, режим или состояние реселлера не разрешает запрос.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Контакт используется существующим доменом или pending/provisioning регистрацией; также возможен конфликт Idempotency-Key.",
            "recovery": "Для того же запроса дождитесь завершения и повторите неизменённый запрос с тем же ключом; изменённый запрос требует нового ключа после сверки результата."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "customer_uuid отсутствует в managed или не разрешён.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ]
      }
    },
    "/balance": {
      "get": {
        "operationId": "getBalance",
        "tags": [
          "Финансы"
        ],
        "summary": "Закупочный баланс реселлера",
        "description": "Только live. Возвращается активный закупочный финансовый счёт реселлера, не баланс конечного покупателя. Пополнение/списание произвольной суммы этим API не предусмотрено. held_minor означает резерв, не окончательное списание.",
        "x-required-scope": "balance.read",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreBalanceEnvelope"
                },
                "examples": {
                  "funded": {
                    "summary": "Средства и резерв",
                    "value": {
                      "data": {
                        "currency": "KZT",
                        "available_minor": 9300000,
                        "held_minor": 700000
                      }
                    }
                  },
                  "zero": {
                    "summary": "Нулевой баланс",
                    "value": {
                      "data": {
                        "currency": "KZT",
                        "available_minor": 0,
                        "held_minor": 0
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Ключ test либо отсутствует balance.read."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Активный финансовый счёт реселлера или связанный счёт покупателя не найден."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Параметры не прошли валидацию или не выполнены бизнес-условия."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Ключ test либо отсутствует balance.read.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Активный финансовый счёт реселлера или связанный счёт покупателя не найден.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Параметры не прошли валидацию или не выполнены бизнес-условия.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ]
      }
    },
    "/ledger": {
      "get": {
        "operationId": "listLedger",
        "tags": [
          "Финансы"
        ],
        "summary": "Проводки закупочного счёта",
        "description": "Только live. Сортировка id по убыванию. Нет фильтров по дате, kind, валюте или customer_uuid. Остатки указаны после каждой проводки, amount_minor сам по себе недостаточен для восстановления движения между available и held. Пагинация cursor/per_page (по умолчанию 25, фактически ограничивается 1..100). Не поддерживаются page, offset, sort, include и произвольные фильтры. При следующем запросе передавайте исходные фильтры вместе с cursor; ссылки не гарантируют сохранение фильтров.",
        "x-required-scope": "billing.read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreLedgerEntryPage"
                },
                "examples": {
                  "last_page": {
                    "summary": "Операции счёта",
                    "value": {
                      "data": [
                        {
                          "uuid": "019a0000-0000-7000-8000-000000000050",
                          "kind": "provider_reserve",
                          "amount_minor": 700000,
                          "currency": "KZT",
                          "available_balance_minor": 9300000,
                          "held_balance_minor": 700000,
                          "description": "Provider reserve.",
                          "occurred_at": "2026-10-05T09:00:00+00:00"
                        }
                      ],
                      "meta": {
                        "next_cursor": null,
                        "per_page": 25
                      }
                    }
                  },
                  "empty": {
                    "summary": "Нет записей в текущем контексте",
                    "value": {
                      "data": [],
                      "meta": {
                        "next_cursor": null,
                        "per_page": 25
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Ключ test либо отсутствует billing.read."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Активный закупочный счёт не найден."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Параметры не прошли валидацию или не выполнены бизнес-условия."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Ключ test либо отсутствует billing.read.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Активный закупочный счёт не найден.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Параметры не прошли валидацию или не выполнены бизнес-условия.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ]
      }
    },
    "/tickets": {
      "get": {
        "operationId": "listTickets",
        "tags": [
          "Поддержка"
        ],
        "summary": "Список обращений",
        "description": "Только live. Публичные тикеты текущего реселлера, по last_message_at убыванию. В списке есть messages_count, но нет messages. Внутренние сообщения исключены. Пагинация cursor/per_page (по умолчанию 25, фактически ограничивается 1..100). Не поддерживаются page, offset, sort, include и произвольные фильтры. При следующем запросе передавайте исходные фильтры вместе с cursor; ссылки не гарантируют сохранение фильтров.",
        "x-required-scope": "tickets.read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          },
          {
            "name": "customer_uuid",
            "in": "query",
            "description": "Фильтр по покупателю.",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "external_id",
            "in": "query",
            "description": "Точное совпадение внешнего ID.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Точное состояние тикета.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreTicketSummaryPage"
                },
                "examples": {
                  "last_page": {
                    "summary": "Тикет без массива messages",
                    "value": {
                      "data": [
                        {
                          "uuid": "019a0000-0000-7000-8000-000000000060",
                          "external_id": "support-4042",
                          "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                          "number": "TCK-20261005090000-DEMO42",
                          "subject": "Проверка регистрации домена",
                          "status": "open",
                          "priority": "normal",
                          "department": null,
                          "category": null,
                          "service_uuid": "019a0000-0000-7000-8000-000000000040",
                          "domain_uuid": "019a0000-0000-7000-8000-000000000041",
                          "order_uuid": "019a0000-0000-7000-8000-000000000030",
                          "last_message_at": "2026-10-05T09:00:00+00:00",
                          "closed_at": null,
                          "messages_count": 1,
                          "created_at": "2026-10-05T09:00:00+00:00",
                          "updated_at": "2026-10-05T09:00:00+00:00"
                        }
                      ],
                      "links": {
                        "first": null,
                        "last": null,
                        "prev": null,
                        "next": null
                      },
                      "meta": {
                        "path": "https://api.example.com/api/reseller/v1/tickets",
                        "per_page": 25,
                        "next_cursor": null,
                        "prev_cursor": null
                      }
                    }
                  },
                  "empty": {
                    "summary": "Нет записей в текущем контексте",
                    "value": {
                      "data": [],
                      "links": {
                        "first": null,
                        "last": null,
                        "prev": null,
                        "next": null
                      },
                      "meta": {
                        "path": "https://api.example.com/api/reseller/v1/tickets",
                        "per_page": 25,
                        "next_cursor": null,
                        "prev_cursor": null
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Ключ test либо отсутствует tickets.read."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Параметры не прошли валидацию или не выполнены бизнес-условия."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Ключ test либо отсутствует tickets.read.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Маршрут отключён конфигурацией либо объект недоступен в текущем контексте.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Параметры не прошли валидацию или не выполнены бизнес-условия.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ]
      },
      "post": {
        "operationId": "createTicket",
        "tags": [
          "Поддержка"
        ],
        "summary": "Создать обращение",
        "description": "Только live. customer_uuid обязателен в managed. Опциональные service_uuid/domain_uuid/order_uuid должны принадлежать тому же покупателю. Нужен владелец покупателя для авторства сообщения. Загрузка вложений в этом reseller API не реализована.",
        "x-required-scope": "tickets.write",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "201": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreTicketEnvelope"
                },
                "examples": {
                  "created": {
                    "summary": "Открытое обращение с первым сообщением",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000060",
                        "external_id": "support-4042",
                        "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                        "number": "TCK-20261005090000-DEMO42",
                        "subject": "Проверка регистрации домена",
                        "status": "open",
                        "priority": "normal",
                        "department": null,
                        "category": null,
                        "service_uuid": "019a0000-0000-7000-8000-000000000040",
                        "domain_uuid": "019a0000-0000-7000-8000-000000000041",
                        "order_uuid": "019a0000-0000-7000-8000-000000000030",
                        "last_message_at": "2026-10-05T09:00:00+00:00",
                        "closed_at": null,
                        "messages_count": 1,
                        "created_at": "2026-10-05T09:00:00+00:00",
                        "updated_at": "2026-10-05T09:00:00+00:00",
                        "messages": [
                          {
                            "uuid": "019a0000-0000-7000-8000-000000000061",
                            "type": "message",
                            "body": "Просим проверить состояние регистрации домена reseller-example.kz.",
                            "sender_type": "customer",
                            "created_at": "2026-10-05T09:00:00+00:00",
                            "attachments": []
                          }
                        ]
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/IdempotencyReplayed"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Ключ test либо отсутствует tickets.write."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Связанный домен/заказ/услуга не найдены у покупателя и реселлера."
          },
          "409": {
            "$ref": "#/components/responses/Error409",
            "description": "Idempotency-Key уже обрабатывается либо использован для другого метода, URL/query или тела."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Неверные длины/приоритет; customer_uuid не найден; нет владельца покупателя; внешний ID уже занят."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Ключ test либо отсутствует tickets.write.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Связанный домен/заказ/услуга не найдены у покупателя и реселлера.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Idempotency-Key уже обрабатывается либо использован для другого метода, URL/query или тела.",
            "recovery": "Для того же запроса дождитесь завершения и повторите неизменённый запрос с тем же ключом; изменённый запрос требует нового ключа после сверки результата."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Неверные длины/приоритет; customer_uuid не найден; нет владельца покупателя; внешний ID уже занят.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CoreCreateTicketRequest"
              },
              "examples": {
                "linked": {
                  "summary": "Обращение о домене",
                  "value": {
                    "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                    "external_id": "support-4042",
                    "subject": "Проверка регистрации домена",
                    "priority": "normal",
                    "message": "Просим проверить состояние регистрации домена reseller-example.kz.",
                    "service_uuid": "019a0000-0000-7000-8000-000000000040",
                    "domain_uuid": "019a0000-0000-7000-8000-000000000041",
                    "order_uuid": "019a0000-0000-7000-8000-000000000030"
                  }
                },
                "unlinked": {
                  "summary": "Без привязки к услуге",
                  "value": {
                    "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                    "subject": "Вопрос по подключению",
                    "priority": "normal",
                    "message": "Просим уточнить условия подключения."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/tickets/{ticketUuid}": {
      "get": {
        "operationId": "getTicket",
        "tags": [
          "Поддержка"
        ],
        "summary": "Получить тикет и переписку",
        "description": "Только live. Включает все публичные сообщения от старых к новым и метаданные вложений. Внутренние заметки исключены; download URL вложений не выдаются.",
        "x-required-scope": "tickets.read",
        "parameters": [
          {
            "name": "ticketUuid",
            "in": "path",
            "required": true,
            "description": "UUID ресурса текущего реселлера.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreTicketEnvelope"
                },
                "examples": {
                  "open": {
                    "summary": "Открытый тикет",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000060",
                        "external_id": "support-4042",
                        "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                        "number": "TCK-20261005090000-DEMO42",
                        "subject": "Проверка регистрации домена",
                        "status": "open",
                        "priority": "normal",
                        "department": null,
                        "category": null,
                        "service_uuid": "019a0000-0000-7000-8000-000000000040",
                        "domain_uuid": "019a0000-0000-7000-8000-000000000041",
                        "order_uuid": "019a0000-0000-7000-8000-000000000030",
                        "last_message_at": "2026-10-05T09:00:00+00:00",
                        "closed_at": null,
                        "messages_count": 1,
                        "created_at": "2026-10-05T09:00:00+00:00",
                        "updated_at": "2026-10-05T09:00:00+00:00",
                        "messages": [
                          {
                            "uuid": "019a0000-0000-7000-8000-000000000061",
                            "type": "message",
                            "body": "Просим проверить состояние регистрации домена reseller-example.kz.",
                            "sender_type": "customer",
                            "created_at": "2026-10-05T09:00:00+00:00",
                            "attachments": []
                          }
                        ]
                      }
                    }
                  },
                  "closed": {
                    "summary": "Закрытый тикет",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000060",
                        "external_id": "support-4042",
                        "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                        "number": "TCK-20261005090000-DEMO42",
                        "subject": "Проверка регистрации домена",
                        "status": "closed",
                        "priority": "normal",
                        "department": null,
                        "category": null,
                        "service_uuid": "019a0000-0000-7000-8000-000000000040",
                        "domain_uuid": "019a0000-0000-7000-8000-000000000041",
                        "order_uuid": "019a0000-0000-7000-8000-000000000030",
                        "last_message_at": "2026-10-05T10:00:00+00:00",
                        "closed_at": "2026-10-05T10:00:00+00:00",
                        "messages_count": 2,
                        "created_at": "2026-10-05T09:00:00+00:00",
                        "updated_at": "2026-10-05T10:00:00+00:00",
                        "messages": [
                          {
                            "uuid": "019a0000-0000-7000-8000-000000000061",
                            "type": "message",
                            "body": "Просим проверить состояние регистрации домена reseller-example.kz.",
                            "sender_type": "customer",
                            "created_at": "2026-10-05T09:00:00+00:00",
                            "attachments": []
                          },
                          {
                            "uuid": "019a0000-0000-7000-8000-000000000062",
                            "type": "message",
                            "body": "Вопрос решён, спасибо.",
                            "sender_type": "customer",
                            "created_at": "2026-10-05T10:00:00+00:00",
                            "attachments": []
                          }
                        ]
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Ключ test либо отсутствует tickets.read."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Тикет не найден у текущего реселлера."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Параметры не прошли валидацию или не выполнены бизнес-условия."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Ключ test либо отсутствует tickets.read.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Тикет не найден у текущего реселлера.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Параметры не прошли валидацию или не выполнены бизнес-условия.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ]
      }
    },
    "/tickets/{ticketUuid}/messages": {
      "post": {
        "operationId": "replyTicket",
        "tags": [
          "Поддержка"
        ],
        "summary": "Ответить и при необходимости закрыть тикет",
        "description": "Только live. Создаётся публичное сообщение от владельца покупателя. close_ticket=true закрывает, false/отсутствие открывает тикет (включая ранее закрытый). Возвращается полная карточка тикета, не только созданное сообщение.",
        "x-required-scope": "tickets.write",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "ticketUuid",
            "in": "path",
            "required": true,
            "description": "UUID ресурса текущего реселлера.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreTicketEnvelope"
                },
                "examples": {
                  "closed": {
                    "summary": "Ответ закрыл обращение",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000060",
                        "external_id": "support-4042",
                        "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                        "number": "TCK-20261005090000-DEMO42",
                        "subject": "Проверка регистрации домена",
                        "status": "closed",
                        "priority": "normal",
                        "department": null,
                        "category": null,
                        "service_uuid": "019a0000-0000-7000-8000-000000000040",
                        "domain_uuid": "019a0000-0000-7000-8000-000000000041",
                        "order_uuid": "019a0000-0000-7000-8000-000000000030",
                        "last_message_at": "2026-10-05T10:00:00+00:00",
                        "closed_at": "2026-10-05T10:00:00+00:00",
                        "messages_count": 2,
                        "created_at": "2026-10-05T09:00:00+00:00",
                        "updated_at": "2026-10-05T10:00:00+00:00",
                        "messages": [
                          {
                            "uuid": "019a0000-0000-7000-8000-000000000061",
                            "type": "message",
                            "body": "Просим проверить состояние регистрации домена reseller-example.kz.",
                            "sender_type": "customer",
                            "created_at": "2026-10-05T09:00:00+00:00",
                            "attachments": []
                          },
                          {
                            "uuid": "019a0000-0000-7000-8000-000000000062",
                            "type": "message",
                            "body": "Вопрос решён, спасибо.",
                            "sender_type": "customer",
                            "created_at": "2026-10-05T10:00:00+00:00",
                            "attachments": []
                          }
                        ]
                      }
                    }
                  },
                  "reopened": {
                    "summary": "Ответ без close_ticket открывает тикет",
                    "value": {
                      "data": {
                        "uuid": "019a0000-0000-7000-8000-000000000060",
                        "external_id": "support-4042",
                        "customer_uuid": "019a0000-0000-7000-8000-000000000010",
                        "number": "TCK-20261005090000-DEMO42",
                        "subject": "Проверка регистрации домена",
                        "status": "open",
                        "priority": "normal",
                        "department": null,
                        "category": null,
                        "service_uuid": "019a0000-0000-7000-8000-000000000040",
                        "domain_uuid": "019a0000-0000-7000-8000-000000000041",
                        "order_uuid": "019a0000-0000-7000-8000-000000000030",
                        "last_message_at": "2026-10-05T10:00:00+00:00",
                        "closed_at": null,
                        "messages_count": 2,
                        "created_at": "2026-10-05T09:00:00+00:00",
                        "updated_at": "2026-10-05T10:00:00+00:00",
                        "messages": [
                          {
                            "uuid": "019a0000-0000-7000-8000-000000000061",
                            "type": "message",
                            "body": "Просим проверить состояние регистрации домена reseller-example.kz.",
                            "sender_type": "customer",
                            "created_at": "2026-10-05T09:00:00+00:00",
                            "attachments": []
                          },
                          {
                            "uuid": "019a0000-0000-7000-8000-000000000062",
                            "type": "message",
                            "body": "Вопрос решён, спасибо.",
                            "sender_type": "customer",
                            "created_at": "2026-10-05T10:00:00+00:00",
                            "attachments": []
                          }
                        ]
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/IdempotencyReplayed"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401",
            "description": "Bearer-ключ отсутствует, недействителен, отозван или истёк."
          },
          "403": {
            "$ref": "#/components/responses/Error403",
            "description": "Ключ test либо отсутствует tickets.write."
          },
          "404": {
            "$ref": "#/components/responses/Error404",
            "description": "Тикет не найден у реселлера."
          },
          "409": {
            "$ref": "#/components/responses/Error409",
            "description": "Idempotency-Key уже обрабатывается либо использован для другого метода, URL/query или тела."
          },
          "422": {
            "$ref": "#/components/responses/Error422",
            "description": "Пустое/слишком длинное сообщение, неверный boolean или у покупателя нет владельца."
          },
          "429": {
            "$ref": "#/components/responses/Error429",
            "description": "Превышен общий лимит текущего API-ключа по всем методам (в том числе replay). Квота отдельных EPP-команд и фоновых задач не равна этому HTTP-лимиту."
          },
          "500": {
            "$ref": "#/components/responses/Error500",
            "description": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта."
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Bearer-ключ отсутствует, недействителен, отозван или истёк.",
            "recovery": "Проверьте ключ и его срок, не повторяйте с теми же неверными credentials."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Ключ test либо отсутствует tickets.write.",
            "recovery": "Проверьте scopes, среду, allowlist и настройки реселлера через администратора."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Тикет не найден у реселлера.",
            "recovery": "Проверьте UUID, среду и принадлежность; при отключённой функции обратитесь к оператору."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Idempotency-Key уже обрабатывается либо использован для другого метода, URL/query или тела.",
            "recovery": "Для того же запроса дождитесь завершения и повторите неизменённый запрос с тем же ключом; изменённый запрос требует нового ключа после сверки результата."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Пустое/слишком длинное сообщение, неверный boolean или у покупателя нет владельца.",
            "recovery": "Исправьте поля из error.details/условия. Не повторяйте неизменённый невалидный запрос."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Лимит текущего API-ключа исчерпан.",
            "recovery": "Учитывайте Retry-After при наличии, используйте backoff с jitter. Запись повторяйте с прежним Idempotency-Key и неизменённым телом."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанная ошибка выполнения; это не подтверждение отсутствия побочного эффекта.",
            "recovery": "Сохраните request_id; сначала проверьте состояние объекта, затем повторяйте запись с тем же ключом. Для неоднозначного результата обращайтесь в поддержку."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CoreReplyTicketRequest"
              },
              "examples": {
                "reply": {
                  "summary": "Ответить",
                  "value": {
                    "message": "Прилагаем уточнение: ошибка повторяется."
                  }
                },
                "close": {
                  "summary": "Ответить и закрыть",
                  "value": {
                    "message": "Вопрос решён, спасибо.",
                    "close_ticket": true
                  }
                }
              }
            }
          }
        }
      }
    },
    "/domains": {
      "get": {
        "tags": [
          "Domains"
        ],
        "summary": "Список доменов",
        "operationId": "listDomains",
        "description": "Требуемые scopes: domains.read.\n\nВозвращает локальные записи только своего провайдера в окружении ключа; сортировка id по убыванию. GET не обращается к реестру. Soft-deleted домены и записи под глобальным migration hold не возвращаются. Фильтры точного совпадения; registry_status и sort не поддерживаются. per_page приводится к integer и ограничивается диапазоном 1..100 (по умолчанию 25). Передавайте непрозрачный cursor без изменений; отсутствие next_cursor означает конец списка.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.read"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          },
          {
            "name": "customer_uuid",
            "in": "query",
            "required": false,
            "description": "Точное совпадение UUID клиента. Невалидный UUID не имеет отдельной валидации; передавайте корректный UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "external_id",
            "in": "query",
            "required": false,
            "description": "Точное совпадение внешнего идентификатора домена.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Локальный status, например active; не EPP status.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "registration_status",
            "in": "query",
            "required": false,
            "description": "Локальный registration_status, например registered.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешное чтение или сохранение настройки.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainListEnvelope"
                },
                "examples": {
                  "DomainList": {
                    "$ref": "#/components/examples/DomainList"
                  },
                  "DomainEmptyList": {
                    "$ref": "#/components/examples/DomainEmptyList"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните доступность API и base URL у оператора."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request GET \"$BASE_URL/domains\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\""
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainController.php"
        ]
      },
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Создать заказ регистрации домена",
        "operationId": "registerDomain",
        "description": "Требуемые scopes: orders.create, domains.register.\n\nЭто alias OrderController::store, а не очередь reseller_domain_operation. Предварительно создайте контакты (contacts.write), запросите quote (quotes.create) с resource_type=domain_zone, operation=register, quantity=1, period_unit=year, period_count=1..10. Payload quote содержит domain_name, четыре contact_uuids и, при необходимости, nameservers 2..6, purpose, external_id, notes, whois_privacy_enabled. Контакты должны быть active и принадлежать клиенту, провайдеру и окружению; зона должна соответствовать имени. Для managed передайте customer_uuid; aggregate использует назначенного клиента. Quote должен быть active, не просрочен, создан этим API-ключом и ещё не использован. external_id запроса идентифицирует заказ; external_id в payload quote идентифицирует услугу. В external checkout заказ рассчитывается и запускается из оптового баланса; platform checkout требует отдельной оплаты счёта. test не расходует live-баланс, но требует отдельного тестового маршрута регистратора. HTTP 201 возвращает Order, не Domain и не Operation. Отслеживайте заказ через /orders/{orderUuid}, услугу /services/{serviceUuid} и домен через список; нужны orders.read/services.read/domains.read. Задача register_domain недоступна через /operations. Цена 500000 в примере условна. При неопределённой регистрации не оформляйте второй заказ; требуется сверка существующего.\n\nКонтроллер общий с /orders: фактически может принять также товарные позиции, при наличии дополнительных scopes (например hosting.order); для доменной интеграции отправляйте только domain_zone/register.\n\nОбязателен Idempotency-Key (1..160 символов). Повтор с тем же ключом должен сохранять метод, путь, query и точные байты JSON. HTTP replay обычно хранится 24 часа и возвращает первоначальный ответ, а не актуальную задачу; проверяйте GET /operations/{operationUuid}. Ключ доменной задачи нельзя переиспользовать после истечения HTTP TTL.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "orders.create",
          "domains.register"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "201": {
            "description": "Запись создана; проверяйте прикладной status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoreOrderEnvelope"
                },
                "examples": {
                  "DomainRegistrationOrder": {
                    "$ref": "#/components/examples/DomainRegistrationOrder"
                  },
                  "DomainRegistrationAwaitingPayment": {
                    "$ref": "#/components/examples/DomainRegistrationAwaitingPayment"
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Присутствует со значением true при возврате сохранённого HTTP-ответа.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Quote истёк/использован/другого окружения; неверная операция, срок, зона, неактивные/чужие контакты; повтор external_id; недостаточно оптового баланса.",
            "recovery": "Исправьте причину, при необходимости пополните баланс и получите новый quote. Проверьте исходный заказ перед повтором."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже используется другим запросом или исходный запрос ещё выполняется.",
            "recovery": "Не изменяйте тело при повторе. Проверьте исходную операцию; используйте новый ключ только для нового намеренного действия."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Невалидный JSON-пayload/поле или отсутствует Idempotency-Key.",
            "recovery": "Исправьте данные по error.details; для изменённого запроса создайте новый ключ."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body; Content-Type: application/json.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CoreCreateOrderRequest"
              },
              "examples": {
                "managed": {
                  "summary": "Регистрация для managed-клиента по ранее выданному quote",
                  "value": {
                    "quote_uuid": "019a1234-1000-7000-8000-000000000006",
                    "customer_uuid": "019a1234-1000-7000-8000-000000000002",
                    "external_id": "crm-order-1001"
                  }
                },
                "aggregate": {
                  "summary": "Регистрация в aggregate-режиме",
                  "value": {
                    "quote_uuid": "019a1234-1000-7000-8000-000000000006",
                    "external_id": "crm-order-1001"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request POST \"$BASE_URL/domains\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-raw '{\n  \"quote_uuid\": \"019a1234-1000-7000-8000-000000000006\",\n  \"customer_uuid\": \"019a1234-1000-7000-8000-000000000002\",\n  \"external_id\": \"crm-order-1001\"\n}'"
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/OrderController.php"
        ],
        "x-prerequisite-scopes": [
          {
            "scope": "quotes.create",
            "when": "Для выпуска quote до вызова этого endpoint."
          },
          {
            "scope": "contacts.write",
            "when": "Если контактные профили ещё не созданы."
          },
          {
            "scope": "domains.read",
            "when": "Для последующего чтения домена/операции."
          }
        ],
        "x-prerequisite-request-example": {
          "description": "Предварительный запрос POST /quotes (другой endpoint, scope quotes.create, отдельный Idempotency-Key). UUID из data.uuid полученного quote передайте в quote_uuid текущей операции. Не используйте здесь демонстрационные UUID или имя без замены.",
          "method": "POST",
          "path": "/quotes",
          "body": {
            "customer_uuid": "019a1234-1000-7000-8000-000000000002",
            "items": [
              {
                "resource_type": "domain_zone",
                "resource_uuid": "019a1234-1000-7000-8000-000000000004",
                "operation": "register",
                "period_unit": "year",
                "period_count": 1,
                "quantity": 1,
                "currency": "KZT",
                "payload": {
                  "domain_name": "example.kz",
                  "contact_uuids": {
                    "owner": "019a1234-1000-7000-8000-000000000003",
                    "admin": "019a1234-1000-7000-8000-000000000003",
                    "tech": "019a1234-1000-7000-8000-000000000003",
                    "billing": "019a1234-1000-7000-8000-000000000003"
                  },
                  "nameservers": [
                    {
                      "hostname": "ns1.example.net"
                    },
                    {
                      "hostname": "ns2.example.net"
                    }
                  ],
                  "external_id": "crm-domain-1001"
                }
              }
            ]
          }
        }
      }
    },
    "/domains/{domainUuid}": {
      "get": {
        "tags": [
          "Domains"
        ],
        "summary": "Получить домен",
        "operationId": "getDomain",
        "description": "Требуемые scopes: domains.read.\n\nЛокальный снимок, не запрос EPP. Для обновления сначала POST /domains/{domainUuid}/sync и дождитесь completed. Ответ содержит все четыре роли контактов; null означает отсутствие профиля. Массив сырых EPP-статусов и auth-код здесь не раскрываются. После подтверждённого удаления этот endpoint возвращает 404; история операций остаётся доступной.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.read"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          }
        ],
        "responses": {
          "200": {
            "description": "Успешное чтение или сохранение настройки.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainEnvelope"
                },
                "examples": {
                  "DomainRegistered": {
                    "$ref": "#/components/examples/DomainRegistered"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request GET \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\""
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainController.php"
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ]
      },
      "delete": {
        "tags": [
          "Domains"
        ],
        "summary": "Удалить домен у регистратора",
        "operationId": "deleteDomain",
        "description": "Требуемые scopes: domains.delete.\n\nРазрушительная операция: запрос удаления в реестр и последующее soft-delete локального домена/услуги после подтверждения. Требуется registered и точное confirm_domain = domain_name. HTTP 202, НИКОГДА не синхронный 204. Подтверждение может означать переход в pendingDelete/redemption, а не немедленную доступность регистрации имени. После completed GET домена может вернуть 404; используйте историю операций. При uncertain повторное удаление не отправляется: выполняется чтение/сверка.\n\nHTTP 202 подтверждает только сохранение задачи, не успешную EPP-команду. Сохраните data.uuid; проверяйте GET /operations/{operationUuid} (domains.read) либо события domain.operation.updated. pending/processing не окончательны; completed подтверждает выполнение, failed означает окончательный отказ. При uncertain не создавайте новую операцию: сверка запланирована, смотрите next_check_at (обычно +5 минут). Для результата, который нельзя доказать чтением/журналом, потребуется поддержка. Зарезервированные средства удерживаются до подтверждения или окончательного отказа.\n\nОбязателен Idempotency-Key (1..160 символов). Повтор с тем же ключом должен сохранять метод, путь, query и точные байты JSON. HTTP replay обычно хранится 24 часа и возвращает первоначальный ответ, а не актуальную задачу; проверяйте GET /operations/{operationUuid}. Ключ доменной задачи нельзя переиспользовать после истечения HTTP TTL.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.delete"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Задача принята; результат реестра проверяется отдельно.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainOperationEnvelope"
                },
                "examples": {
                  "DomainAcceptedDeleteDomain": {
                    "$ref": "#/components/examples/DomainAcceptedDeleteDomain"
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Присутствует со значением true при возврате сохранённого HTTP-ответа.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Интеграция домена неактивна, migration hold или есть pending/processing/uncertain/awaiting_registry задача.",
            "recovery": "Дождитесь исходной задачи либо обратитесь к оператору. sync и управление текущим трансфером разрешены при занятой очереди."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Драйвер не поддерживает действие либо домен не зарегистрирован для требующего регистрации действия.",
            "recovery": "Проверьте capabilities и состояние домена; не вызывайте неподдерживаемое действие."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже используется другим запросом или исходный запрос ещё выполняется.",
            "recovery": "Не изменяйте тело при повторе. Проверьте исходную операцию; используйте новый ключ только для нового намеренного действия."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Невалидный JSON-пayload/поле или отсутствует Idempotency-Key.",
            "recovery": "Исправьте данные по error.details; для изменённого запроса создайте новый ключ."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "confirm_domain не совпадает побайтно с domain_name.",
            "recovery": "Прочитайте текущий домен и подтвердите точное имя; выполняйте удаление только по явному поручению владельца."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body; Content-Type: application/json.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainDeleteRequest"
              },
              "examples": {
                "request": {
                  "summary": "Удалить домен у регистратора",
                  "value": {
                    "confirm_domain": "example.kz",
                    "reason": "Явное поручение владельца удалить домен"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request DELETE \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-raw '{\n  \"confirm_domain\": \"example.kz\",\n  \"reason\": \"Явное поручение владельца удалить домен\"\n}'"
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-async-error-cases": [
          {
            "status": "failed",
            "code": "validation_failed",
            "when": "Обработчик отклонил входные данные/состояние либо получил отказ, классифицированный как ValidationException.",
            "recovery": "Изучите ограничения и журнал через поддержку; новый запрос только после окончательного отказа и устранения причины."
          },
          {
            "status": "failed",
            "code": "registry_rejected",
            "when": "Есть относящийся к этой задаче подтверждённый отказ реестра либо ошибка до отправки.",
            "recovery": "Не повторяйте неизменённую команду; исправьте причину. Резерв освобождается."
          },
          {
            "status": "uncertain",
            "code": "registry_result_unknown",
            "when": "После начала действия нет надёжного подтверждения, произошёл timeout, идентичность ответа не совпала либо сверка пока не установила результат.",
            "recovery": "Не посылайте вторую мутацию. Опрос исходного UUID после next_check_at; при длительном ожидании передайте UUID и request_id поддержке. Резерв не освобождается автоматически."
          }
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ]
      }
    },
    "/domains/check": {
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Проверить доступность имени",
        "operationId": "checkDomain",
        "description": "Требуемые scopes: domains.read.\n\nСинхронный запрос регистратора через маршрут зоны для окружения ключа. Проверка не резервирует имя и не гарантирует последующую регистрацию. HTTP 200 с available=false является нормальным результатом. При replay по прежнему ключу возвращается старая проверка; для новой проверки используйте новый ключ. Не проверяет баланс/контакты/готовность заказа. Отсутствующий маршрут/сбой драйвера может дать 500, а не available=false.\n\nОбязателен Idempotency-Key (1..160 символов). Повтор с тем же ключом должен сохранять метод, путь, query и точные байты JSON. HTTP replay обычно хранится 24 часа и возвращает первоначальный ответ, а не актуальную задачу; проверяйте GET /operations/{operationUuid}. Ключ доменной задачи нельзя переиспользовать после истечения HTTP TTL.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.read"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Успешное чтение или сохранение настройки.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainCheckEnvelope"
                },
                "examples": {
                  "DomainAvailable": {
                    "$ref": "#/components/examples/DomainAvailable"
                  },
                  "DomainUnavailable": {
                    "$ref": "#/components/examples/DomainUnavailable"
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Присутствует со значением true при возврате сохранённого HTTP-ответа.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже используется другим запросом или исходный запрос ещё выполняется.",
            "recovery": "Не изменяйте тело при повторе. Проверьте исходную операцию; используйте новый ключ только для нового намеренного действия."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Невалидный JSON-пayload/поле или отсутствует Idempotency-Key.",
            "recovery": "Исправьте данные по error.details; для изменённого запроса создайте новый ключ."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните доступность API и base URL у оператора."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body; Content-Type: application/json.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainCheckRequest"
              },
              "examples": {
                "check": {
                  "summary": "Проверка демонстрационного имени",
                  "value": {
                    "domain": "example.kz"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request POST \"$BASE_URL/domains/check\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-raw '{\n  \"domain\": \"example.kz\"\n}'"
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainController.php"
        ]
      }
    },
    "/domains/{domainUuid}/renew": {
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Продлить домен",
        "operationId": "renewDomain",
        "description": "Требуемые scopes: domains.renew.\n\nQuote operation=renew, payload.domain_service_uuid этого домена; period_unit=year, period_count=1..10. Продление возможно не ранее registered_at +24 часа, при известной expires_at; новый срок expires_at + N лет не должен быть позже now +10 календарных лет. Доменные и сервисные статусы должны быть active/expired, registration_status=registered, зона допускает renewal. clientRenewProhibited, serverRenewProhibited, pendingDelete, redemptionPeriod, pendingTransfer запрещают продление. Ещё раз проверяется актуальное состояние реестра перед EPP renew. Нужен provider.checkout_mode=external и действующий одноразовый quote того же API-ключа, окружения, провайдера, клиента и зоны: ровно одна позиция domain_zone с quantity=1. Цену и срок берите из quote, не вычисляйте по цене регистрации. В live резервируется оптовый баланс, после подтверждения резерв списывается; при окончательном отказе освобождается. В test live-баланс не используется.\n\nHTTP 202 подтверждает только сохранение задачи, не успешную EPP-команду. Сохраните data.uuid; проверяйте GET /operations/{operationUuid} (domains.read) либо события domain.operation.updated. pending/processing не окончательны; completed подтверждает выполнение, failed означает окончательный отказ. При uncertain не создавайте новую операцию: сверка запланирована, смотрите next_check_at (обычно +5 минут). Для результата, который нельзя доказать чтением/журналом, потребуется поддержка. Зарезервированные средства удерживаются до подтверждения или окончательного отказа.\n\nОбязателен Idempotency-Key (1..160 символов). Повтор с тем же ключом должен сохранять метод, путь, query и точные байты JSON. HTTP replay обычно хранится 24 часа и возвращает первоначальный ответ, а не актуальную задачу; проверяйте GET /operations/{operationUuid}. Ключ доменной задачи нельзя переиспользовать после истечения HTTP TTL.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.renew"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Задача принята; результат реестра проверяется отдельно.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainOperationEnvelope"
                },
                "examples": {
                  "DomainAcceptedRenewDomain": {
                    "$ref": "#/components/examples/DomainAcceptedRenewDomain"
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Присутствует со значением true при возврате сохранённого HTTP-ответа.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Интеграция домена неактивна, migration hold или есть pending/processing/uncertain/awaiting_registry задача.",
            "recovery": "Дождитесь исходной задачи либо обратитесь к оператору. sync и управление текущим трансфером разрешены при занятой очереди."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Драйвер не поддерживает действие либо домен не зарегистрирован для требующего регистрации действия.",
            "recovery": "Проверьте capabilities и состояние домена; не вызывайте неподдерживаемое действие."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Quote не соответствует домену/зоне/операции, истёк/использован; checkout не external; недостаточно средств либо нарушены ограничения срока.",
            "recovery": "Получите корректный quote/пополните баланс/дождитесь допустимого срока. При неизвестном результате старой операции новую не создавайте."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже используется другим запросом или исходный запрос ещё выполняется.",
            "recovery": "Не изменяйте тело при повторе. Проверьте исходную операцию; используйте новый ключ только для нового намеренного действия."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Невалидный JSON-пayload/поле или отсутствует Idempotency-Key.",
            "recovery": "Исправьте данные по error.details; для изменённого запроса создайте новый ключ."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body; Content-Type: application/json.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainRenewRequest"
              },
              "examples": {
                "request": {
                  "summary": "Продлить домен",
                  "value": {
                    "quote_uuid": "019a1234-1000-7000-8000-000000000006",
                    "reason": "Клиент продлевает домен на год"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request POST \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/renew\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-raw '{\n  \"quote_uuid\": \"019a1234-1000-7000-8000-000000000006\",\n  \"reason\": \"Клиент продлевает домен на год\"\n}'"
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-async-error-cases": [
          {
            "status": "failed",
            "code": "validation_failed",
            "when": "Обработчик отклонил входные данные/состояние либо получил отказ, классифицированный как ValidationException.",
            "recovery": "Изучите ограничения и журнал через поддержку; новый запрос только после окончательного отказа и устранения причины."
          },
          {
            "status": "failed",
            "code": "registry_rejected",
            "when": "Есть относящийся к этой задаче подтверждённый отказ реестра либо ошибка до отправки.",
            "recovery": "Не повторяйте неизменённую команду; исправьте причину. Резерв освобождается."
          },
          {
            "status": "uncertain",
            "code": "registry_result_unknown",
            "when": "После начала действия нет надёжного подтверждения, произошёл timeout, идентичность ответа не совпала либо сверка пока не установила результат.",
            "recovery": "Не посылайте вторую мутацию. Опрос исходного UUID после next_check_at; при длительном ожидании передайте UUID и request_id поддержке. Резерв не освобождается автоматически."
          }
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ],
        "x-prerequisite-request-example": {
          "description": "Предварительный запрос POST /quotes (другой endpoint, scope quotes.create, отдельный Idempotency-Key). UUID из data.uuid полученного quote передайте в quote_uuid текущей операции. Не используйте здесь демонстрационные UUID или имя без замены.",
          "method": "POST",
          "path": "/quotes",
          "body": {
            "customer_uuid": "019a1234-1000-7000-8000-000000000002",
            "items": [
              {
                "resource_type": "domain_zone",
                "resource_uuid": "019a1234-1000-7000-8000-000000000004",
                "operation": "renew",
                "period_unit": "year",
                "period_count": 1,
                "quantity": 1,
                "currency": "KZT",
                "payload": {
                  "domain_service_uuid": "019a1234-1000-7000-8000-000000000001"
                }
              }
            ]
          }
        }
      }
    },
    "/domains/{domainUuid}/transfer": {
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Запросить трансфер существующей локальной записи",
        "operationId": "transferDomain",
        "description": "Требуемые scopes: domains.transfer.\n\nДля уже существующей локальной записи. Для отсутствующей в биллинге используйте /domains/transfers. Quote operation=transfer с payload.domain_service_uuid, period_unit=year/period_count=1 является тарифом одной операции, а не гарантией увеличения срока на год. auth_code получите у текущего регистратора. Окончание трансфера подтверждается transfer query и domain info, а не HTTP 202. Нужен provider.checkout_mode=external и действующий одноразовый quote того же API-ключа, окружения, провайдера, клиента и зоны: ровно одна позиция domain_zone с quantity=1. Цену и срок берите из quote, не вычисляйте по цене регистрации. В live резервируется оптовый баланс, после подтверждения резерв списывается; при окончательном отказе освобождается. В test live-баланс не используется.\n\nHTTP 202 подтверждает только сохранение задачи, не успешную EPP-команду. Сохраните data.uuid; проверяйте GET /operations/{operationUuid} (domains.read) либо события domain.operation.updated. pending/processing не окончательны; completed подтверждает выполнение, failed означает окончательный отказ. При uncertain не создавайте новую операцию: сверка запланирована, смотрите next_check_at (обычно +5 минут). Для результата, который нельзя доказать чтением/журналом, потребуется поддержка. Зарезервированные средства удерживаются до подтверждения или окончательного отказа.\n\nОбязателен Idempotency-Key (1..160 символов). Повтор с тем же ключом должен сохранять метод, путь, query и точные байты JSON. HTTP replay обычно хранится 24 часа и возвращает первоначальный ответ, а не актуальную задачу; проверяйте GET /operations/{operationUuid}. Ключ доменной задачи нельзя переиспользовать после истечения HTTP TTL.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.\n\nНа стадии обработчика дополнительно проверяются transfer_enabled зоны, hold_status=none и registration_status в registered/pending. Эти проверки могут завершить уже принятую задачу failed; это не обязательно синхронный HTTP 422.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.transfer"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Задача принята; результат реестра проверяется отдельно.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainOperationEnvelope"
                },
                "examples": {
                  "DomainAcceptedTransferDomain": {
                    "$ref": "#/components/examples/DomainAcceptedTransferDomain"
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Присутствует со значением true при возврате сохранённого HTTP-ответа.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Интеграция домена неактивна, migration hold или есть pending/processing/uncertain/awaiting_registry задача.",
            "recovery": "Дождитесь исходной задачи либо обратитесь к оператору. sync и управление текущим трансфером разрешены при занятой очереди."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Драйвер не поддерживает действие либо домен не зарегистрирован для требующего регистрации действия.",
            "recovery": "Проверьте capabilities и состояние домена; не вызывайте неподдерживаемое действие."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Quote не соответствует домену/зоне/операции, истёк/использован; checkout не external; недостаточно средств либо нарушены ограничения срока.",
            "recovery": "Получите корректный quote/пополните баланс/дождитесь допустимого срока. При неизвестном результате старой операции новую не создавайте."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже используется другим запросом или исходный запрос ещё выполняется.",
            "recovery": "Не изменяйте тело при повторе. Проверьте исходную операцию; используйте новый ключ только для нового намеренного действия."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Невалидный JSON-пayload/поле или отсутствует Idempotency-Key.",
            "recovery": "Исправьте данные по error.details; для изменённого запроса создайте новый ключ."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body; Content-Type: application/json.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainTransferRequest"
              },
              "examples": {
                "request": {
                  "summary": "Запросить трансфер существующей локальной записи",
                  "value": {
                    "quote_uuid": "019a1234-1000-7000-8000-000000000006",
                    "auth_code": "DEMO-REPLACE-WITH-CURRENT-REGISTRAR-CODE",
                    "reason": "Перенос домена по заявке клиента"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request POST \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/transfer\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-raw '{\n  \"quote_uuid\": \"019a1234-1000-7000-8000-000000000006\",\n  \"auth_code\": \"DEMO-REPLACE-WITH-CURRENT-REGISTRAR-CODE\",\n  \"reason\": \"Перенос домена по заявке клиента\"\n}'"
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-async-error-cases": [
          {
            "status": "failed",
            "code": "validation_failed",
            "when": "Обработчик отклонил входные данные/состояние либо получил отказ, классифицированный как ValidationException.",
            "recovery": "Изучите ограничения и журнал через поддержку; новый запрос только после окончательного отказа и устранения причины."
          },
          {
            "status": "failed",
            "code": "registry_rejected",
            "when": "Есть относящийся к этой задаче подтверждённый отказ реестра либо ошибка до отправки.",
            "recovery": "Не повторяйте неизменённую команду; исправьте причину. Резерв освобождается."
          },
          {
            "status": "uncertain",
            "code": "registry_result_unknown",
            "when": "После начала действия нет надёжного подтверждения, произошёл timeout, идентичность ответа не совпала либо сверка пока не установила результат.",
            "recovery": "Не посылайте вторую мутацию. Опрос исходного UUID после next_check_at; при длительном ожидании передайте UUID и request_id поддержке. Резерв не освобождается автоматически."
          },
          {
            "status": "failed",
            "code": "transfer_rejected",
            "when": "Сверка исходного transfer увидела rejected/canceled.",
            "recovery": "Резерв освобождается; выясните причину у текущего регистратора."
          }
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ],
        "x-prerequisite-request-example": {
          "description": "Предварительный запрос POST /quotes (другой endpoint, scope quotes.create, отдельный Idempotency-Key). UUID из data.uuid полученного quote передайте в quote_uuid текущей операции. Не используйте здесь демонстрационные UUID или имя без замены.",
          "method": "POST",
          "path": "/quotes",
          "body": {
            "customer_uuid": "019a1234-1000-7000-8000-000000000002",
            "items": [
              {
                "resource_type": "domain_zone",
                "resource_uuid": "019a1234-1000-7000-8000-000000000004",
                "operation": "transfer",
                "period_unit": "year",
                "period_count": 1,
                "quantity": 1,
                "currency": "KZT",
                "payload": {
                  "domain_service_uuid": "019a1234-1000-7000-8000-000000000001"
                }
              }
            ]
          }
        }
      }
    },
    "/domains/{domainUuid}/restore": {
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Восстановить домен из периода восстановления",
        "operationId": "restoreDomain",
        "description": "Требуемые scopes: domains.restore.\n\nДоступны принадлежащие провайдеру soft-deleted записи. Зарегистрированный домен вне redemptionPeriod/pendingDelete не допускается. Требуется отдельный quote operation=restore, payload.domain_service_uuid, year/1; используются цены restore, не fallback renewal. Передайте restore_auth или restore_password по требованиям регистратора. Успех подтверждается отсутствием redemptionPeriod/pendingDelete/pendingRestore; это не гарантирует определённое продление срока, смотрите expires_at. Нужен provider.checkout_mode=external и действующий одноразовый quote того же API-ключа, окружения, провайдера, клиента и зоны: ровно одна позиция domain_zone с quantity=1. Цену и срок берите из quote, не вычисляйте по цене регистрации. В live резервируется оптовый баланс, после подтверждения резерв списывается; при окончательном отказе освобождается. В test live-баланс не используется.\n\nHTTP 202 подтверждает только сохранение задачи, не успешную EPP-команду. Сохраните data.uuid; проверяйте GET /operations/{operationUuid} (domains.read) либо события domain.operation.updated. pending/processing не окончательны; completed подтверждает выполнение, failed означает окончательный отказ. При uncertain не создавайте новую операцию: сверка запланирована, смотрите next_check_at (обычно +5 минут). Для результата, который нельзя доказать чтением/журналом, потребуется поддержка. Зарезервированные средства удерживаются до подтверждения или окончательного отказа.\n\nОбязателен Idempotency-Key (1..160 символов). Повтор с тем же ключом должен сохранять метод, путь, query и точные байты JSON. HTTP replay обычно хранится 24 часа и возвращает первоначальный ответ, а не актуальную задачу; проверяйте GET /operations/{operationUuid}. Ключ доменной задачи нельзя переиспользовать после истечения HTTP TTL.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.restore"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Задача принята; результат реестра проверяется отдельно.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainOperationEnvelope"
                },
                "examples": {
                  "DomainAcceptedRestoreDomain": {
                    "$ref": "#/components/examples/DomainAcceptedRestoreDomain"
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Присутствует со значением true при возврате сохранённого HTTP-ответа.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Интеграция домена неактивна, migration hold или есть pending/processing/uncertain/awaiting_registry задача.",
            "recovery": "Дождитесь исходной задачи либо обратитесь к оператору. sync и управление текущим трансфером разрешены при занятой очереди."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Драйвер не поддерживает действие либо домен не зарегистрирован для требующего регистрации действия.",
            "recovery": "Проверьте capabilities и состояние домена; не вызывайте неподдерживаемое действие."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Quote не соответствует домену/зоне/операции, истёк/использован; checkout не external; недостаточно средств либо нарушены ограничения срока.",
            "recovery": "Получите корректный quote/пополните баланс/дождитесь допустимого срока. При неизвестном результате старой операции новую не создавайте."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже используется другим запросом или исходный запрос ещё выполняется.",
            "recovery": "Не изменяйте тело при повторе. Проверьте исходную операцию; используйте новый ключ только для нового намеренного действия."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Невалидный JSON-пayload/поле или отсутствует Idempotency-Key.",
            "recovery": "Исправьте данные по error.details; для изменённого запроса создайте новый ключ."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body; Content-Type: application/json.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainRestoreRequest"
              },
              "examples": {
                "request": {
                  "summary": "Восстановить домен из периода восстановления",
                  "value": {
                    "quote_uuid": "019a1234-1000-7000-8000-000000000006",
                    "restore_auth": "DEMO-RESTORE-AUTH",
                    "reason": "Восстановление по заявке владельца"
                  }
                },
                "password": {
                  "summary": "Восстановление с альтернативным паролем",
                  "value": {
                    "quote_uuid": "019a1234-1000-7000-8000-000000000006",
                    "restore_password": "DEMO-RESTORE-PASSWORD",
                    "reason": "Восстановление по заявке владельца"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request POST \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/restore\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-raw '{\n  \"quote_uuid\": \"019a1234-1000-7000-8000-000000000006\",\n  \"restore_auth\": \"DEMO-RESTORE-AUTH\",\n  \"reason\": \"Восстановление по заявке владельца\"\n}'"
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-async-error-cases": [
          {
            "status": "failed",
            "code": "validation_failed",
            "when": "Обработчик отклонил входные данные/состояние либо получил отказ, классифицированный как ValidationException.",
            "recovery": "Изучите ограничения и журнал через поддержку; новый запрос только после окончательного отказа и устранения причины."
          },
          {
            "status": "failed",
            "code": "registry_rejected",
            "when": "Есть относящийся к этой задаче подтверждённый отказ реестра либо ошибка до отправки.",
            "recovery": "Не повторяйте неизменённую команду; исправьте причину. Резерв освобождается."
          },
          {
            "status": "uncertain",
            "code": "registry_result_unknown",
            "when": "После начала действия нет надёжного подтверждения, произошёл timeout, идентичность ответа не совпала либо сверка пока не установила результат.",
            "recovery": "Не посылайте вторую мутацию. Опрос исходного UUID после next_check_at; при длительном ожидании передайте UUID и request_id поддержке. Резерв не освобождается автоматически."
          }
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ],
        "x-prerequisite-request-example": {
          "description": "Предварительный запрос POST /quotes (другой endpoint, scope quotes.create, отдельный Idempotency-Key). UUID из data.uuid полученного quote передайте в quote_uuid текущей операции. Не используйте здесь демонстрационные UUID или имя без замены.",
          "method": "POST",
          "path": "/quotes",
          "body": {
            "customer_uuid": "019a1234-1000-7000-8000-000000000002",
            "items": [
              {
                "resource_type": "domain_zone",
                "resource_uuid": "019a1234-1000-7000-8000-000000000004",
                "operation": "restore",
                "period_unit": "year",
                "period_count": 1,
                "quantity": 1,
                "currency": "KZT",
                "payload": {
                  "domain_service_uuid": "019a1234-1000-7000-8000-000000000001"
                }
              }
            ]
          }
        }
      }
    },
    "/domains/{domainUuid}/nameservers": {
      "put": {
        "tags": [
          "Domains"
        ],
        "summary": "Заменить делегирование NS",
        "operationId": "updateDomainNameservers",
        "description": "Требуемые scopes: domains.manage.\n\nПолная замена набора NS (2..6), а не добавление одного сервера. Имена нормализуются в Punycode и сравниваются без регистра; дубликаты запрещены. ipv4/ipv6 необязательны и nullable. Это делегирование, не редактирование A/MX/TXT; распространение DNS не входит в срок выполнения задачи.\n\nHTTP 202 подтверждает только сохранение задачи, не успешную EPP-команду. Сохраните data.uuid; проверяйте GET /operations/{operationUuid} (domains.read) либо события domain.operation.updated. pending/processing не окончательны; completed подтверждает выполнение, failed означает окончательный отказ. При uncertain не создавайте новую операцию: сверка запланирована, смотрите next_check_at (обычно +5 минут). Для результата, который нельзя доказать чтением/журналом, потребуется поддержка. Зарезервированные средства удерживаются до подтверждения или окончательного отказа.\n\nОбязателен Idempotency-Key (1..160 символов). Повтор с тем же ключом должен сохранять метод, путь, query и точные байты JSON. HTTP replay обычно хранится 24 часа и возвращает первоначальный ответ, а не актуальную задачу; проверяйте GET /operations/{operationUuid}. Ключ доменной задачи нельзя переиспользовать после истечения HTTP TTL.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.manage"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Задача принята; результат реестра проверяется отдельно.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainOperationEnvelope"
                },
                "examples": {
                  "DomainAcceptedUpdateDomainNameservers": {
                    "$ref": "#/components/examples/DomainAcceptedUpdateDomainNameservers"
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Присутствует со значением true при возврате сохранённого HTTP-ответа.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Интеграция домена неактивна, migration hold или есть pending/processing/uncertain/awaiting_registry задача.",
            "recovery": "Дождитесь исходной задачи либо обратитесь к оператору. sync и управление текущим трансфером разрешены при занятой очереди."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Драйвер не поддерживает действие либо домен не зарегистрирован для требующего регистрации действия.",
            "recovery": "Проверьте capabilities и состояние домена; не вызывайте неподдерживаемое действие."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже используется другим запросом или исходный запрос ещё выполняется.",
            "recovery": "Не изменяйте тело при повторе. Проверьте исходную операцию; используйте новый ключ только для нового намеренного действия."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Невалидный JSON-пayload/поле или отсутствует Idempotency-Key.",
            "recovery": "Исправьте данные по error.details; для изменённого запроса создайте новый ключ."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Список NS не содержит 2..6 элементов, hostname некорректен/повторяется после нормализации либо адрес не соответствует ipv4/ipv6.",
            "recovery": "Исправьте весь набор NS; повтор с изменённым JSON выполняйте под новым ключом."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body; Content-Type: application/json.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainNameserversRequest"
              },
              "examples": {
                "request": {
                  "summary": "Заменить делегирование NS",
                  "value": {
                    "nameservers": [
                      {
                        "hostname": "ns1.example.net",
                        "ipv4": null,
                        "ipv6": null
                      },
                      {
                        "hostname": "ns2.example.net",
                        "ipv4": null,
                        "ipv6": null
                      }
                    ],
                    "reason": "Смена DNS-провайдера"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request PUT \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/nameservers\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-raw '{\n  \"nameservers\": [\n    {\n      \"hostname\": \"ns1.example.net\",\n      \"ipv4\": null,\n      \"ipv6\": null\n    },\n    {\n      \"hostname\": \"ns2.example.net\",\n      \"ipv4\": null,\n      \"ipv6\": null\n    }\n  ],\n  \"reason\": \"Смена DNS-провайдера\"\n}'"
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-async-error-cases": [
          {
            "status": "failed",
            "code": "validation_failed",
            "when": "Обработчик отклонил входные данные/состояние либо получил отказ, классифицированный как ValidationException.",
            "recovery": "Изучите ограничения и журнал через поддержку; новый запрос только после окончательного отказа и устранения причины."
          },
          {
            "status": "failed",
            "code": "registry_rejected",
            "when": "Есть относящийся к этой задаче подтверждённый отказ реестра либо ошибка до отправки.",
            "recovery": "Не повторяйте неизменённую команду; исправьте причину. Резерв освобождается."
          },
          {
            "status": "uncertain",
            "code": "registry_result_unknown",
            "when": "После начала действия нет надёжного подтверждения, произошёл timeout, идентичность ответа не совпала либо сверка пока не установила результат.",
            "recovery": "Не посылайте вторую мутацию. Опрос исходного UUID после next_check_at; при длительном ожидании передайте UUID и request_id поддержке. Резерв не освобождается автоматически."
          }
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ]
      }
    },
    "/domains/{domainUuid}/contacts": {
      "put": {
        "tags": [
          "Domains"
        ],
        "summary": "Изменить контакты зарегистрированного домена",
        "operationId": "updateDomainContacts",
        "description": "Требуемые scopes: domains.manage.\n\nПередайте все четыре роли контактов; частичная замена не поддержана. UUID должны принадлежать тому же клиенту, провайдеру и окружению. Можно использовать один UUID для всех ролей. Смена владельца и подтверждённого профиля подчиняется дополнительным правилам регистратора; 202 не гарантирует разрешение смены владельца.\n\nHTTP 202 подтверждает только сохранение задачи, не успешную EPP-команду. Сохраните data.uuid; проверяйте GET /operations/{operationUuid} (domains.read) либо события domain.operation.updated. pending/processing не окончательны; completed подтверждает выполнение, failed означает окончательный отказ. При uncertain не создавайте новую операцию: сверка запланирована, смотрите next_check_at (обычно +5 минут). Для результата, который нельзя доказать чтением/журналом, потребуется поддержка. Зарезервированные средства удерживаются до подтверждения или окончательного отказа.\n\nОбязателен Idempotency-Key (1..160 символов). Повтор с тем же ключом должен сохранять метод, путь, query и точные байты JSON. HTTP replay обычно хранится 24 часа и возвращает первоначальный ответ, а не актуальную задачу; проверяйте GET /operations/{operationUuid}. Ключ доменной задачи нельзя переиспользовать после истечения HTTP TTL.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.\n\nЕсли локальные и реестровые роли уже совпадают с запросом, менеджер считает это отсутствием изменений и может завершить задачу отказом, а не успешным no-op. Для изменения контактов драйвер должен дополнительно поддерживать DomainInfo; capability contacts отражает только базовый интерфейс. Смена владельца/контактов может сбросить прежнюю проверку и потребовать новой ЭЦП.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.manage"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Задача принята; результат реестра проверяется отдельно.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainOperationEnvelope"
                },
                "examples": {
                  "DomainAcceptedUpdateDomainContacts": {
                    "$ref": "#/components/examples/DomainAcceptedUpdateDomainContacts"
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Присутствует со значением true при возврате сохранённого HTTP-ответа.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Интеграция домена неактивна, migration hold или есть pending/processing/uncertain/awaiting_registry задача.",
            "recovery": "Дождитесь исходной задачи либо обратитесь к оператору. sync и управление текущим трансфером разрешены при занятой очереди."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Драйвер не поддерживает действие либо домен не зарегистрирован для требующего регистрации действия.",
            "recovery": "Проверьте capabilities и состояние домена; не вызывайте неподдерживаемое действие."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже используется другим запросом или исходный запрос ещё выполняется.",
            "recovery": "Не изменяйте тело при повторе. Проверьте исходную операцию; используйте новый ключ только для нового намеренного действия."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Невалидный JSON-пayload/поле или отсутствует Idempotency-Key.",
            "recovery": "Исправьте данные по error.details; для изменённого запроса создайте новый ключ."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Не переданы все четыре роли, переданы посторонние роли или контакт не принадлежит клиенту/провайдеру/окружению.",
            "recovery": "Создайте/выберите корректные профили и передайте полный набор ролей."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body; Content-Type: application/json.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainContactsRequest"
              },
              "examples": {
                "request": {
                  "summary": "Изменить контакты зарегистрированного домена",
                  "value": {
                    "contacts": {
                      "owner": "019a1234-1000-7000-8000-000000000003",
                      "admin": "019a1234-1000-7000-8000-000000000003",
                      "tech": "019a1234-1000-7000-8000-000000000003",
                      "billing": "019a1234-1000-7000-8000-000000000003"
                    },
                    "reason": "Актуализация контактов владельца"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request PUT \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/contacts\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-raw '{\n  \"contacts\": {\n    \"owner\": \"019a1234-1000-7000-8000-000000000003\",\n    \"admin\": \"019a1234-1000-7000-8000-000000000003\",\n    \"tech\": \"019a1234-1000-7000-8000-000000000003\",\n    \"billing\": \"019a1234-1000-7000-8000-000000000003\"\n  },\n  \"reason\": \"Актуализация контактов владельца\"\n}'"
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-async-error-cases": [
          {
            "status": "failed",
            "code": "validation_failed",
            "when": "Обработчик отклонил входные данные/состояние либо получил отказ, классифицированный как ValidationException.",
            "recovery": "Изучите ограничения и журнал через поддержку; новый запрос только после окончательного отказа и устранения причины."
          },
          {
            "status": "failed",
            "code": "registry_rejected",
            "when": "Есть относящийся к этой задаче подтверждённый отказ реестра либо ошибка до отправки.",
            "recovery": "Не повторяйте неизменённую команду; исправьте причину. Резерв освобождается."
          },
          {
            "status": "uncertain",
            "code": "registry_result_unknown",
            "when": "После начала действия нет надёжного подтверждения, произошёл timeout, идентичность ответа не совпала либо сверка пока не установила результат.",
            "recovery": "Не посылайте вторую мутацию. Опрос исходного UUID после next_check_at; при длительном ожидании передайте UUID и request_id поддержке. Резерв не освобождается автоматически."
          }
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ]
      }
    },
    "/domains/{domainUuid}/statuses": {
      "put": {
        "tags": [
          "Domains"
        ],
        "summary": "Добавить или снять client-статусы",
        "operationId": "updateDomainStatuses",
        "description": "Требуемые scopes: domains.manage.\n\nРазрешены только пять перечисленных client-статусов; server*-статусы управляются реестром. Передайте непустой add или remove; каждый до 5 уникальных значений; одно значение нельзя одновременно добавить и удалить. Снятие статуса не отменяет независимые ограничения реестра.\n\nHTTP 202 подтверждает только сохранение задачи, не успешную EPP-команду. Сохраните data.uuid; проверяйте GET /operations/{operationUuid} (domains.read) либо события domain.operation.updated. pending/processing не окончательны; completed подтверждает выполнение, failed означает окончательный отказ. При uncertain не создавайте новую операцию: сверка запланирована, смотрите next_check_at (обычно +5 минут). Для результата, который нельзя доказать чтением/журналом, потребуется поддержка. Зарезервированные средства удерживаются до подтверждения или окончательного отказа.\n\nОбязателен Idempotency-Key (1..160 символов). Повтор с тем же ключом должен сохранять метод, путь, query и точные байты JSON. HTTP replay обычно хранится 24 часа и возвращает первоначальный ответ, а не актуальную задачу; проверяйте GET /operations/{operationUuid}. Ключ доменной задачи нельзя переиспользовать после истечения HTTP TTL.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.\n\nОсобенность текущей реализации: снятие clientHold идёт как custom-изменение; локальный hold_status не обязательно очищается одновременно. После завершения выполните sync и проверьте состояние; при расхождении обратитесь в поддержку. HTTP 202 не является гарантией снятия всех локальных ограничений.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.manage"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Задача принята; результат реестра проверяется отдельно.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainOperationEnvelope"
                },
                "examples": {
                  "DomainAcceptedUpdateDomainStatuses": {
                    "$ref": "#/components/examples/DomainAcceptedUpdateDomainStatuses"
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Присутствует со значением true при возврате сохранённого HTTP-ответа.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Интеграция домена неактивна, migration hold или есть pending/processing/uncertain/awaiting_registry задача.",
            "recovery": "Дождитесь исходной задачи либо обратитесь к оператору. sync и управление текущим трансфером разрешены при занятой очереди."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Драйвер не поддерживает действие либо домен не зарегистрирован для требующего регистрации действия.",
            "recovery": "Проверьте capabilities и состояние домена; не вызывайте неподдерживаемое действие."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже используется другим запросом или исходный запрос ещё выполняется.",
            "recovery": "Не изменяйте тело при повторе. Проверьте исходную операцию; используйте новый ключ только для нового намеренного действия."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Невалидный JSON-пayload/поле или отсутствует Idempotency-Key.",
            "recovery": "Исправьте данные по error.details; для изменённого запроса создайте новый ключ."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Недопустимый client-статус, дубликаты либо пересечение add/remove.",
            "recovery": "Разделите добавление/удаление и используйте только разрешённые client-статусы."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body; Content-Type: application/json.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainStatusesRequest"
              },
              "examples": {
                "request": {
                  "summary": "Добавить или снять client-статусы",
                  "value": {
                    "add": [
                      "clientTransferProhibited"
                    ],
                    "reason": "Владелец запрещает трансфер"
                  }
                },
                "unlock": {
                  "summary": "Снять запрет трансфера",
                  "value": {
                    "remove": [
                      "clientTransferProhibited"
                    ],
                    "reason": "Владелец разрешает перенос"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request PUT \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/statuses\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-raw '{\n  \"add\": [\n    \"clientTransferProhibited\"\n  ],\n  \"reason\": \"Владелец запрещает трансфер\"\n}'"
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-async-error-cases": [
          {
            "status": "failed",
            "code": "validation_failed",
            "when": "Обработчик отклонил входные данные/состояние либо получил отказ, классифицированный как ValidationException.",
            "recovery": "Изучите ограничения и журнал через поддержку; новый запрос только после окончательного отказа и устранения причины."
          },
          {
            "status": "failed",
            "code": "registry_rejected",
            "when": "Есть относящийся к этой задаче подтверждённый отказ реестра либо ошибка до отправки.",
            "recovery": "Не повторяйте неизменённую команду; исправьте причину. Резерв освобождается."
          },
          {
            "status": "uncertain",
            "code": "registry_result_unknown",
            "when": "После начала действия нет надёжного подтверждения, произошёл timeout, идентичность ответа не совпала либо сверка пока не установила результат.",
            "recovery": "Не посылайте вторую мутацию. Опрос исходного UUID после next_check_at; при длительном ожидании передайте UUID и request_id поддержке. Резерв не освобождается автоматически."
          }
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ]
      }
    },
    "/domains/{domainUuid}/whois-privacy": {
      "put": {
        "tags": [
          "Domains"
        ],
        "summary": "Настроить скрытие контактных данных WHOIS",
        "operationId": "setDomainWhoisPrivacy",
        "description": "Требуемые scopes: domains.manage.\n\nИспользуется политика раскрытия контактов регистратора. Поля name/organization/address/phone/fax/email. Применение может быть частичным и перейти в uncertain; это не обещание удалить уже опубликованные данные из чужих WHOIS-кэшей. Текущее локальное состояние читайте capabilities.whois_privacy.\n\nHTTP 202 подтверждает только сохранение задачи, не успешную EPP-команду. Сохраните data.uuid; проверяйте GET /operations/{operationUuid} (domains.read) либо события domain.operation.updated. pending/processing не окончательны; completed подтверждает выполнение, failed означает окончательный отказ. При uncertain не создавайте новую операцию: сверка запланирована, смотрите next_check_at (обычно +5 минут). Для результата, который нельзя доказать чтением/журналом, потребуется поддержка. Зарезервированные средства удерживаются до подтверждения или окончательного отказа.\n\nОбязателен Idempotency-Key (1..160 символов). Повтор с тем же ключом должен сохранять метод, путь, query и точные байты JSON. HTTP replay обычно хранится 24 часа и возвращает первоначальный ответ, а не актуальную задачу; проверяйте GET /operations/{operationUuid}. Ключ доменной задачи нельзя переиспользовать после истечения HTTP TTL.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.manage"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Задача принята; результат реестра проверяется отдельно.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainOperationEnvelope"
                },
                "examples": {
                  "DomainAcceptedSetDomainWhoisPrivacy": {
                    "$ref": "#/components/examples/DomainAcceptedSetDomainWhoisPrivacy"
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Присутствует со значением true при возврате сохранённого HTTP-ответа.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Интеграция домена неактивна, migration hold или есть pending/processing/uncertain/awaiting_registry задача.",
            "recovery": "Дождитесь исходной задачи либо обратитесь к оператору. sync и управление текущим трансфером разрешены при занятой очереди."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Драйвер не поддерживает действие либо домен не зарегистрирован для требующего регистрации действия.",
            "recovery": "Проверьте capabilities и состояние домена; не вызывайте неподдерживаемое действие."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже используется другим запросом или исходный запрос ещё выполняется.",
            "recovery": "Не изменяйте тело при повторе. Проверьте исходную операцию; используйте новый ключ только для нового намеренного действия."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Невалидный JSON-пayload/поле или отсутствует Idempotency-Key.",
            "recovery": "Исправьте данные по error.details; для изменённого запроса создайте новый ключ."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Не передан enabled либо hidden_fields содержит неизвестное/повторяющееся поле.",
            "recovery": "Используйте boolean enabled и поля из capabilities.whois_privacy.available_fields."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body; Content-Type: application/json.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainPrivacyRequest"
              },
              "examples": {
                "request": {
                  "summary": "Настроить скрытие контактных данных WHOIS",
                  "value": {
                    "enabled": true,
                    "hidden_fields": [
                      "email",
                      "phone"
                    ],
                    "reason": "Запрос владельца на конфиденциальность"
                  }
                },
                "disable": {
                  "summary": "Снять скрытие полей",
                  "value": {
                    "enabled": false,
                    "reason": "Владелец разрешает публикацию контактов"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request PUT \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/whois-privacy\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-raw '{\n  \"enabled\": true,\n  \"hidden_fields\": [\n    \"email\",\n    \"phone\"\n  ],\n  \"reason\": \"Запрос владельца на конфиденциальность\"\n}'"
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-async-error-cases": [
          {
            "status": "failed",
            "code": "validation_failed",
            "when": "Обработчик отклонил входные данные/состояние либо получил отказ, классифицированный как ValidationException.",
            "recovery": "Изучите ограничения и журнал через поддержку; новый запрос только после окончательного отказа и устранения причины."
          },
          {
            "status": "failed",
            "code": "registry_rejected",
            "when": "Есть относящийся к этой задаче подтверждённый отказ реестра либо ошибка до отправки.",
            "recovery": "Не повторяйте неизменённую команду; исправьте причину. Резерв освобождается."
          },
          {
            "status": "uncertain",
            "code": "registry_result_unknown",
            "when": "После начала действия нет надёжного подтверждения, произошёл timeout, идентичность ответа не совпала либо сверка пока не установила результат.",
            "recovery": "Не посылайте вторую мутацию. Опрос исходного UUID после next_check_at; при длительном ожидании передайте UUID и request_id поддержке. Резерв не освобождается автоматически."
          }
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ]
      }
    },
    "/domains/{domainUuid}/sync": {
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Обновить локальные сведения из реестра",
        "operationId": "synchronizeDomain",
        "description": "Требуемые scopes: domains.read.\n\nСинхронизация ставится в очередь, хотя scope только domains.read; Idempotency-Key и reason обязательны. Разрешена при другой незавершённой задаче; сначала проверяется идентичность домена/регистратора/объекта. После completed повторите GET домена.\n\nHTTP 202 подтверждает только сохранение задачи, не успешную EPP-команду. Сохраните data.uuid; проверяйте GET /operations/{operationUuid} (domains.read) либо события domain.operation.updated. pending/processing не окончательны; completed подтверждает выполнение, failed означает окончательный отказ. При uncertain не создавайте новую операцию: сверка запланирована, смотрите next_check_at (обычно +5 минут). Для результата, который нельзя доказать чтением/журналом, потребуется поддержка. Зарезервированные средства удерживаются до подтверждения или окончательного отказа.\n\nОбязателен Idempotency-Key (1..160 символов). Повтор с тем же ключом должен сохранять метод, путь, query и точные байты JSON. HTTP replay обычно хранится 24 часа и возвращает первоначальный ответ, а не актуальную задачу; проверяйте GET /operations/{operationUuid}. Ключ доменной задачи нельзя переиспользовать после истечения HTTP TTL.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.read"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Задача принята; результат реестра проверяется отдельно.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainOperationEnvelope"
                },
                "examples": {
                  "DomainAcceptedSynchronizeDomain": {
                    "$ref": "#/components/examples/DomainAcceptedSynchronizeDomain"
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Присутствует со значением true при возврате сохранённого HTTP-ответа.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Интеграция домена неактивна, migration hold или есть pending/processing/uncertain/awaiting_registry задача.",
            "recovery": "Дождитесь исходной задачи либо обратитесь к оператору. sync и управление текущим трансфером разрешены при занятой очереди."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Драйвер не поддерживает действие либо домен не зарегистрирован для требующего регистрации действия.",
            "recovery": "Проверьте capabilities и состояние домена; не вызывайте неподдерживаемое действие."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже используется другим запросом или исходный запрос ещё выполняется.",
            "recovery": "Не изменяйте тело при повторе. Проверьте исходную операцию; используйте новый ключ только для нового намеренного действия."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Невалидный JSON-пayload/поле или отсутствует Idempotency-Key.",
            "recovery": "Исправьте данные по error.details; для изменённого запроса создайте новый ключ."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body; Content-Type: application/json.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainReasonRequest"
              },
              "examples": {
                "request": {
                  "summary": "Обновить локальные сведения из реестра",
                  "value": {
                    "reason": "Проверка актуального срока и состояния"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request POST \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/sync\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-raw '{\n  \"reason\": \"Проверка актуального срока и состояния\"\n}'"
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-async-error-cases": [
          {
            "status": "failed",
            "code": "validation_failed",
            "when": "Обработчик отклонил входные данные/состояние либо получил отказ, классифицированный как ValidationException.",
            "recovery": "Изучите ограничения и журнал через поддержку; новый запрос только после окончательного отказа и устранения причины."
          },
          {
            "status": "failed",
            "code": "registry_rejected",
            "when": "Есть относящийся к этой задаче подтверждённый отказ реестра либо ошибка до отправки.",
            "recovery": "Не повторяйте неизменённую команду; исправьте причину. Резерв освобождается."
          },
          {
            "status": "uncertain",
            "code": "registry_result_unknown",
            "when": "После начала действия нет надёжного подтверждения, произошёл timeout, идентичность ответа не совпала либо сверка пока не установила результат.",
            "recovery": "Не посылайте вторую мутацию. Опрос исходного UUID после next_check_at; при длительном ожидании передайте UUID и request_id поддержке. Резерв не освобождается автоматически."
          }
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ]
      }
    },
    "/domains/{domainUuid}/auth-code": {
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Обновить и получить код трансфера",
        "operationId": "refreshDomainAuthCode",
        "description": "Требуемые scopes: domains.auth-code.\n\nОперация вызывает refresh auth-кода и может изменить секрет в реестре; это не обычное чтение. В ответе POST код никогда не раскрывается. После completed получите /operations/{operationUuid} с ОБОИМИ scopes domains.read и domains.auth-code. Без дополнительного scope result.auth_code отсутствует даже у завершённой операции. Не выводите токен/код в логах.\n\nHTTP 202 подтверждает только сохранение задачи, не успешную EPP-команду. Сохраните data.uuid; проверяйте GET /operations/{operationUuid} (domains.read) либо события domain.operation.updated. pending/processing не окончательны; completed подтверждает выполнение, failed означает окончательный отказ. При uncertain не создавайте новую операцию: сверка запланирована, смотрите next_check_at (обычно +5 минут). Для результата, который нельзя доказать чтением/журналом, потребуется поддержка. Зарезервированные средства удерживаются до подтверждения или окончательного отказа.\n\nОбязателен Idempotency-Key (1..160 символов). Повтор с тем же ключом должен сохранять метод, путь, query и точные байты JSON. HTTP replay обычно хранится 24 часа и возвращает первоначальный ответ, а не актуальную задачу; проверяйте GET /operations/{operationUuid}. Ключ доменной задачи нельзя переиспользовать после истечения HTTP TTL.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.auth-code"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Задача принята; результат реестра проверяется отдельно.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainOperationEnvelope"
                },
                "examples": {
                  "DomainAcceptedRefreshDomainAuthCode": {
                    "$ref": "#/components/examples/DomainAcceptedRefreshDomainAuthCode"
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Присутствует со значением true при возврате сохранённого HTTP-ответа.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Интеграция домена неактивна, migration hold или есть pending/processing/uncertain/awaiting_registry задача.",
            "recovery": "Дождитесь исходной задачи либо обратитесь к оператору. sync и управление текущим трансфером разрешены при занятой очереди."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Драйвер не поддерживает действие либо домен не зарегистрирован для требующего регистрации действия.",
            "recovery": "Проверьте capabilities и состояние домена; не вызывайте неподдерживаемое действие."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже используется другим запросом или исходный запрос ещё выполняется.",
            "recovery": "Не изменяйте тело при повторе. Проверьте исходную операцию; используйте новый ключ только для нового намеренного действия."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Невалидный JSON-пayload/поле или отсутствует Idempotency-Key.",
            "recovery": "Исправьте данные по error.details; для изменённого запроса создайте новый ключ."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body; Content-Type: application/json.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainReasonRequest"
              },
              "examples": {
                "request": {
                  "summary": "Обновить и получить код трансфера",
                  "value": {
                    "reason": "Владелец запросил перенос к другому регистратору"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request POST \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/auth-code\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-raw '{\n  \"reason\": \"Владелец запросил перенос к другому регистратору\"\n}'"
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-async-error-cases": [
          {
            "status": "failed",
            "code": "validation_failed",
            "when": "Обработчик отклонил входные данные/состояние либо получил отказ, классифицированный как ValidationException.",
            "recovery": "Изучите ограничения и журнал через поддержку; новый запрос только после окончательного отказа и устранения причины."
          },
          {
            "status": "failed",
            "code": "registry_rejected",
            "when": "Есть относящийся к этой задаче подтверждённый отказ реестра либо ошибка до отправки.",
            "recovery": "Не повторяйте неизменённую команду; исправьте причину. Резерв освобождается."
          },
          {
            "status": "uncertain",
            "code": "registry_result_unknown",
            "when": "После начала действия нет надёжного подтверждения, произошёл timeout, идентичность ответа не совпала либо сверка пока не установила результат.",
            "recovery": "Не посылайте вторую мутацию. Опрос исходного UUID после next_check_at; при длительном ожидании передайте UUID и request_id поддержке. Резерв не освобождается автоматически."
          }
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ]
      }
    },
    "/domains/{domainUuid}/transfer/query": {
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Запросить состояние текущего трансфера",
        "operationId": "queryDomainTransfer",
        "description": "Требуемые scopes: domains.transfer.\n\nЗапрос состояния поставлен в очередь, не непосредственный EPP-ответ. Завершение query-задачи не означает завершение самого трансфера; после completed читайте domain.registry_status. Самостоятельная query не заменяет отслеживание исходной оплаченной transfer-задачи.\n\nHTTP 202 подтверждает только сохранение задачи, не успешную EPP-команду. Сохраните data.uuid; проверяйте GET /operations/{operationUuid} (domains.read) либо события domain.operation.updated. pending/processing не окончательны; completed подтверждает выполнение, failed означает окончательный отказ. При uncertain не создавайте новую операцию: сверка запланирована, смотрите next_check_at (обычно +5 минут). Для результата, который нельзя доказать чтением/журналом, потребуется поддержка. Зарезервированные средства удерживаются до подтверждения или окончательного отказа.\n\nОбязателен Idempotency-Key (1..160 символов). Повтор с тем же ключом должен сохранять метод, путь, query и точные байты JSON. HTTP replay обычно хранится 24 часа и возвращает первоначальный ответ, а не актуальную задачу; проверяйте GET /operations/{operationUuid}. Ключ доменной задачи нельзя переиспользовать после истечения HTTP TTL.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.\n\nНа стадии обработчика дополнительно проверяются transfer_enabled зоны, hold_status=none и registration_status в registered/pending. Эти проверки могут завершить уже принятую задачу failed; это не обязательно синхронный HTTP 422.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.transfer"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Задача принята; результат реестра проверяется отдельно.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainOperationEnvelope"
                },
                "examples": {
                  "DomainAcceptedQueryDomainTransfer": {
                    "$ref": "#/components/examples/DomainAcceptedQueryDomainTransfer"
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Присутствует со значением true при возврате сохранённого HTTP-ответа.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Интеграция домена неактивна, migration hold или есть pending/processing/uncertain/awaiting_registry задача.",
            "recovery": "Дождитесь исходной задачи либо обратитесь к оператору. sync и управление текущим трансфером разрешены при занятой очереди."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Драйвер не поддерживает действие либо домен не зарегистрирован для требующего регистрации действия.",
            "recovery": "Проверьте capabilities и состояние домена; не вызывайте неподдерживаемое действие."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже используется другим запросом или исходный запрос ещё выполняется.",
            "recovery": "Не изменяйте тело при повторе. Проверьте исходную операцию; используйте новый ключ только для нового намеренного действия."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Невалидный JSON-пayload/поле или отсутствует Idempotency-Key.",
            "recovery": "Исправьте данные по error.details; для изменённого запроса создайте новый ключ."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body; Content-Type: application/json.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainReasonRequest"
              },
              "examples": {
                "request": {
                  "summary": "Запросить состояние текущего трансфера",
                  "value": {
                    "reason": "Уточнение состояния трансфера"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request POST \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/transfer/query\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-raw '{\n  \"reason\": \"Уточнение состояния трансфера\"\n}'"
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-async-error-cases": [
          {
            "status": "failed",
            "code": "validation_failed",
            "when": "Обработчик отклонил входные данные/состояние либо получил отказ, классифицированный как ValidationException.",
            "recovery": "Изучите ограничения и журнал через поддержку; новый запрос только после окончательного отказа и устранения причины."
          },
          {
            "status": "failed",
            "code": "registry_rejected",
            "when": "Есть относящийся к этой задаче подтверждённый отказ реестра либо ошибка до отправки.",
            "recovery": "Не повторяйте неизменённую команду; исправьте причину. Резерв освобождается."
          },
          {
            "status": "uncertain",
            "code": "registry_result_unknown",
            "when": "После начала действия нет надёжного подтверждения, произошёл timeout, идентичность ответа не совпала либо сверка пока не установила результат.",
            "recovery": "Не посылайте вторую мутацию. Опрос исходного UUID после next_check_at; при длительном ожидании передайте UUID и request_id поддержке. Резерв не освобождается автоматически."
          }
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ]
      }
    },
    "/domains/{domainUuid}/transfer/approve": {
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Подтвердить текущий трансфер",
        "operationId": "approveDomainTransfer",
        "description": "Требуемые scopes: domains.transfer.\n\nКоманда approve текущего трансфера, когда это разрешено текущему регистратору. Не создаёт входящий трансфер и не принимает новый auth_code. Разрешена при незавершённой исходной transfer-задаче; её финансовый результат отслеживайте отдельно.\n\nHTTP 202 подтверждает только сохранение задачи, не успешную EPP-команду. Сохраните data.uuid; проверяйте GET /operations/{operationUuid} (domains.read) либо события domain.operation.updated. pending/processing не окончательны; completed подтверждает выполнение, failed означает окончательный отказ. При uncertain не создавайте новую операцию: сверка запланирована, смотрите next_check_at (обычно +5 минут). Для результата, который нельзя доказать чтением/журналом, потребуется поддержка. Зарезервированные средства удерживаются до подтверждения или окончательного отказа.\n\nОбязателен Idempotency-Key (1..160 символов). Повтор с тем же ключом должен сохранять метод, путь, query и точные байты JSON. HTTP replay обычно хранится 24 часа и возвращает первоначальный ответ, а не актуальную задачу; проверяйте GET /operations/{operationUuid}. Ключ доменной задачи нельзя переиспользовать после истечения HTTP TTL.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.\n\nНа стадии обработчика дополнительно проверяются transfer_enabled зоны, hold_status=none и registration_status в registered/pending. Эти проверки могут завершить уже принятую задачу failed; это не обязательно синхронный HTTP 422.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.transfer"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Задача принята; результат реестра проверяется отдельно.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainOperationEnvelope"
                },
                "examples": {
                  "DomainAcceptedApproveDomainTransfer": {
                    "$ref": "#/components/examples/DomainAcceptedApproveDomainTransfer"
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Присутствует со значением true при возврате сохранённого HTTP-ответа.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Интеграция домена неактивна, migration hold или есть pending/processing/uncertain/awaiting_registry задача.",
            "recovery": "Дождитесь исходной задачи либо обратитесь к оператору. sync и управление текущим трансфером разрешены при занятой очереди."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Драйвер не поддерживает действие либо домен не зарегистрирован для требующего регистрации действия.",
            "recovery": "Проверьте capabilities и состояние домена; не вызывайте неподдерживаемое действие."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже используется другим запросом или исходный запрос ещё выполняется.",
            "recovery": "Не изменяйте тело при повторе. Проверьте исходную операцию; используйте новый ключ только для нового намеренного действия."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Невалидный JSON-пayload/поле или отсутствует Idempotency-Key.",
            "recovery": "Исправьте данные по error.details; для изменённого запроса создайте новый ключ."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body; Content-Type: application/json.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainReasonRequest"
              },
              "examples": {
                "request": {
                  "summary": "Подтвердить текущий трансфер",
                  "value": {
                    "reason": "Владелец подтверждает исходящий трансфер"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request POST \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/transfer/approve\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-raw '{\n  \"reason\": \"Владелец подтверждает исходящий трансфер\"\n}'"
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-async-error-cases": [
          {
            "status": "failed",
            "code": "validation_failed",
            "when": "Обработчик отклонил входные данные/состояние либо получил отказ, классифицированный как ValidationException.",
            "recovery": "Изучите ограничения и журнал через поддержку; новый запрос только после окончательного отказа и устранения причины."
          },
          {
            "status": "failed",
            "code": "registry_rejected",
            "when": "Есть относящийся к этой задаче подтверждённый отказ реестра либо ошибка до отправки.",
            "recovery": "Не повторяйте неизменённую команду; исправьте причину. Резерв освобождается."
          },
          {
            "status": "uncertain",
            "code": "registry_result_unknown",
            "when": "После начала действия нет надёжного подтверждения, произошёл timeout, идентичность ответа не совпала либо сверка пока не установила результат.",
            "recovery": "Не посылайте вторую мутацию. Опрос исходного UUID после next_check_at; при длительном ожидании передайте UUID и request_id поддержке. Резерв не освобождается автоматически."
          }
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ]
      }
    },
    "/domains/{domainUuid}/transfer/reject": {
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Отклонить текущий трансфер",
        "operationId": "rejectDomainTransfer",
        "description": "Требуемые scopes: domains.transfer.\n\nКоманда reject; допустимость зависит от роли регистратора и состояния трансфера. Завершение управляющей задачи не эквивалентно немедленному освобождению резерва исходной transfer-задачи; дождитесь её сверки.\n\nHTTP 202 подтверждает только сохранение задачи, не успешную EPP-команду. Сохраните data.uuid; проверяйте GET /operations/{operationUuid} (domains.read) либо события domain.operation.updated. pending/processing не окончательны; completed подтверждает выполнение, failed означает окончательный отказ. При uncertain не создавайте новую операцию: сверка запланирована, смотрите next_check_at (обычно +5 минут). Для результата, который нельзя доказать чтением/журналом, потребуется поддержка. Зарезервированные средства удерживаются до подтверждения или окончательного отказа.\n\nОбязателен Idempotency-Key (1..160 символов). Повтор с тем же ключом должен сохранять метод, путь, query и точные байты JSON. HTTP replay обычно хранится 24 часа и возвращает первоначальный ответ, а не актуальную задачу; проверяйте GET /operations/{operationUuid}. Ключ доменной задачи нельзя переиспользовать после истечения HTTP TTL.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.\n\nНа стадии обработчика дополнительно проверяются transfer_enabled зоны, hold_status=none и registration_status в registered/pending. Эти проверки могут завершить уже принятую задачу failed; это не обязательно синхронный HTTP 422.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.transfer"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Задача принята; результат реестра проверяется отдельно.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainOperationEnvelope"
                },
                "examples": {
                  "DomainAcceptedRejectDomainTransfer": {
                    "$ref": "#/components/examples/DomainAcceptedRejectDomainTransfer"
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Присутствует со значением true при возврате сохранённого HTTP-ответа.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Интеграция домена неактивна, migration hold или есть pending/processing/uncertain/awaiting_registry задача.",
            "recovery": "Дождитесь исходной задачи либо обратитесь к оператору. sync и управление текущим трансфером разрешены при занятой очереди."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Драйвер не поддерживает действие либо домен не зарегистрирован для требующего регистрации действия.",
            "recovery": "Проверьте capabilities и состояние домена; не вызывайте неподдерживаемое действие."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже используется другим запросом или исходный запрос ещё выполняется.",
            "recovery": "Не изменяйте тело при повторе. Проверьте исходную операцию; используйте новый ключ только для нового намеренного действия."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Невалидный JSON-пayload/поле или отсутствует Idempotency-Key.",
            "recovery": "Исправьте данные по error.details; для изменённого запроса создайте новый ключ."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body; Content-Type: application/json.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainReasonRequest"
              },
              "examples": {
                "request": {
                  "summary": "Отклонить текущий трансфер",
                  "value": {
                    "reason": "Владелец отклоняет несанкционированный перенос"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request POST \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/transfer/reject\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-raw '{\n  \"reason\": \"Владелец отклоняет несанкционированный перенос\"\n}'"
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-async-error-cases": [
          {
            "status": "failed",
            "code": "validation_failed",
            "when": "Обработчик отклонил входные данные/состояние либо получил отказ, классифицированный как ValidationException.",
            "recovery": "Изучите ограничения и журнал через поддержку; новый запрос только после окончательного отказа и устранения причины."
          },
          {
            "status": "failed",
            "code": "registry_rejected",
            "when": "Есть относящийся к этой задаче подтверждённый отказ реестра либо ошибка до отправки.",
            "recovery": "Не повторяйте неизменённую команду; исправьте причину. Резерв освобождается."
          },
          {
            "status": "uncertain",
            "code": "registry_result_unknown",
            "when": "После начала действия нет надёжного подтверждения, произошёл timeout, идентичность ответа не совпала либо сверка пока не установила результат.",
            "recovery": "Не посылайте вторую мутацию. Опрос исходного UUID после next_check_at; при длительном ожидании передайте UUID и request_id поддержке. Резерв не освобождается автоматически."
          }
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ]
      }
    },
    "/domains/{domainUuid}/transfer/cancel": {
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Отменить текущий запрос трансфера",
        "operationId": "cancelDomainTransfer",
        "description": "Требуемые scopes: domains.transfer.\n\nКоманда cancel существующего трансфера; не отмена произвольной provisioning-задачи. Реестр может запретить отмену. Читайте исходную transfer-задачу и domain.registry_status.\n\nHTTP 202 подтверждает только сохранение задачи, не успешную EPP-команду. Сохраните data.uuid; проверяйте GET /operations/{operationUuid} (domains.read) либо события domain.operation.updated. pending/processing не окончательны; completed подтверждает выполнение, failed означает окончательный отказ. При uncertain не создавайте новую операцию: сверка запланирована, смотрите next_check_at (обычно +5 минут). Для результата, который нельзя доказать чтением/журналом, потребуется поддержка. Зарезервированные средства удерживаются до подтверждения или окончательного отказа.\n\nОбязателен Idempotency-Key (1..160 символов). Повтор с тем же ключом должен сохранять метод, путь, query и точные байты JSON. HTTP replay обычно хранится 24 часа и возвращает первоначальный ответ, а не актуальную задачу; проверяйте GET /operations/{operationUuid}. Ключ доменной задачи нельзя переиспользовать после истечения HTTP TTL.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.\n\nНа стадии обработчика дополнительно проверяются transfer_enabled зоны, hold_status=none и registration_status в registered/pending. Эти проверки могут завершить уже принятую задачу failed; это не обязательно синхронный HTTP 422.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.transfer"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Задача принята; результат реестра проверяется отдельно.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainOperationEnvelope"
                },
                "examples": {
                  "DomainAcceptedCancelDomainTransfer": {
                    "$ref": "#/components/examples/DomainAcceptedCancelDomainTransfer"
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Присутствует со значением true при возврате сохранённого HTTP-ответа.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Интеграция домена неактивна, migration hold или есть pending/processing/uncertain/awaiting_registry задача.",
            "recovery": "Дождитесь исходной задачи либо обратитесь к оператору. sync и управление текущим трансфером разрешены при занятой очереди."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Драйвер не поддерживает действие либо домен не зарегистрирован для требующего регистрации действия.",
            "recovery": "Проверьте capabilities и состояние домена; не вызывайте неподдерживаемое действие."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже используется другим запросом или исходный запрос ещё выполняется.",
            "recovery": "Не изменяйте тело при повторе. Проверьте исходную операцию; используйте новый ключ только для нового намеренного действия."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Невалидный JSON-пayload/поле или отсутствует Idempotency-Key.",
            "recovery": "Исправьте данные по error.details; для изменённого запроса создайте новый ключ."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body; Content-Type: application/json.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainReasonRequest"
              },
              "examples": {
                "request": {
                  "summary": "Отменить текущий запрос трансфера",
                  "value": {
                    "reason": "Клиент отменил запрос переноса"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request POST \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/transfer/cancel\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-raw '{\n  \"reason\": \"Клиент отменил запрос переноса\"\n}'"
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-async-error-cases": [
          {
            "status": "failed",
            "code": "validation_failed",
            "when": "Обработчик отклонил входные данные/состояние либо получил отказ, классифицированный как ValidationException.",
            "recovery": "Изучите ограничения и журнал через поддержку; новый запрос только после окончательного отказа и устранения причины."
          },
          {
            "status": "failed",
            "code": "registry_rejected",
            "when": "Есть относящийся к этой задаче подтверждённый отказ реестра либо ошибка до отправки.",
            "recovery": "Не повторяйте неизменённую команду; исправьте причину. Резерв освобождается."
          },
          {
            "status": "uncertain",
            "code": "registry_result_unknown",
            "when": "После начала действия нет надёжного подтверждения, произошёл timeout, идентичность ответа не совпала либо сверка пока не установила результат.",
            "recovery": "Не посылайте вторую мутацию. Опрос исходного UUID после next_check_at; при длительном ожидании передайте UUID и request_id поддержке. Резерв не освобождается автоматически."
          }
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ]
      }
    },
    "/domains/{domainUuid}/hosts": {
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Создать subordinate glue-host",
        "operationId": "createDomainHost",
        "description": "Требуемые scopes: domains.manage.\n\nРазрешён только hostname, оканчивающийся на точку + Punycode текущего домена: ns1.example.kz, не example.kz и не ns1.other.kz. Нужен хотя бы один адрес. Это EPP host-объект (glue), не DNS-запись A/AAAA зоны и не автоматическая смена делегирования. Адреса из примера зарезервированы для документации и должны быть заменены.\n\nHTTP 202 подтверждает только сохранение задачи, не успешную EPP-команду. Сохраните data.uuid; проверяйте GET /operations/{operationUuid} (domains.read) либо события domain.operation.updated. pending/processing не окончательны; completed подтверждает выполнение, failed означает окончательный отказ. При uncertain не создавайте новую операцию: сверка запланирована, смотрите next_check_at (обычно +5 минут). Для результата, который нельзя доказать чтением/журналом, потребуется поддержка. Зарезервированные средства удерживаются до подтверждения или окончательного отказа.\n\nОбязателен Idempotency-Key (1..160 символов). Повтор с тем же ключом должен сохранять метод, путь, query и точные байты JSON. HTTP replay обычно хранится 24 часа и возвращает первоначальный ответ, а не актуальную задачу; проверяйте GET /operations/{operationUuid}. Ключ доменной задачи нельзя переиспользовать после истечения HTTP TTL.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.manage"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Задача принята; результат реестра проверяется отдельно.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainOperationEnvelope"
                },
                "examples": {
                  "DomainAcceptedCreateDomainHost": {
                    "$ref": "#/components/examples/DomainAcceptedCreateDomainHost"
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Присутствует со значением true при возврате сохранённого HTTP-ответа.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Интеграция домена неактивна, migration hold или есть pending/processing/uncertain/awaiting_registry задача.",
            "recovery": "Дождитесь исходной задачи либо обратитесь к оператору. sync и управление текущим трансфером разрешены при занятой очереди."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Драйвер не поддерживает действие либо домен не зарегистрирован для требующего регистрации действия.",
            "recovery": "Проверьте capabilities и состояние домена; не вызывайте неподдерживаемое действие."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже используется другим запросом или исходный запрос ещё выполняется.",
            "recovery": "Не изменяйте тело при повторе. Проверьте исходную операцию; используйте новый ключ только для нового намеренного действия."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Невалидный JSON-пayload/поле или отсутствует Idempotency-Key.",
            "recovery": "Исправьте данные по error.details; для изменённого запроса создайте новый ключ."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Hostname не является subordinate host текущего домена. Не передан ни один IP либо IP невалиден.",
            "recovery": "Используйте имя вида ns1.example.kz для домена example.kz и корректные адреса; на чужой домен сменой hostname доступ не получить."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body; Content-Type: application/json.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainHostCreateRequest"
              },
              "examples": {
                "request": {
                  "summary": "Создать subordinate glue-host",
                  "value": {
                    "hostname": "ns1.example.kz",
                    "ipv4": "192.0.2.10",
                    "ipv6": "2001:db8::10",
                    "reason": "Собственный сервер имён клиента"
                  }
                },
                "ipv6Only": {
                  "summary": "Host только с IPv6",
                  "value": {
                    "hostname": "ns1.example.kz",
                    "ipv6": "2001:db8::10",
                    "reason": "Создание IPv6 сервера имён"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request POST \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/hosts\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-raw '{\n  \"hostname\": \"ns1.example.kz\",\n  \"ipv4\": \"192.0.2.10\",\n  \"ipv6\": \"2001:db8::10\",\n  \"reason\": \"Собственный сервер имён клиента\"\n}'"
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-async-error-cases": [
          {
            "status": "failed",
            "code": "validation_failed",
            "when": "Обработчик отклонил входные данные/состояние либо получил отказ, классифицированный как ValidationException.",
            "recovery": "Изучите ограничения и журнал через поддержку; новый запрос только после окончательного отказа и устранения причины."
          },
          {
            "status": "failed",
            "code": "registry_rejected",
            "when": "Есть относящийся к этой задаче подтверждённый отказ реестра либо ошибка до отправки.",
            "recovery": "Не повторяйте неизменённую команду; исправьте причину. Резерв освобождается."
          },
          {
            "status": "uncertain",
            "code": "registry_result_unknown",
            "when": "После начала действия нет надёжного подтверждения, произошёл timeout, идентичность ответа не совпала либо сверка пока не установила результат.",
            "recovery": "Не посылайте вторую мутацию. Опрос исходного UUID после next_check_at; при длительном ожидании передайте UUID и request_id поддержке. Резерв не освобождается автоматически."
          }
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ]
      },
      "put": {
        "tags": [
          "Domains"
        ],
        "summary": "Изменить адреса glue-host",
        "operationId": "updateDomainHost",
        "description": "Требуемые scopes: domains.manage.\n\nМеняет набор IP существующего subordinate host. Передайте непустой add_addresses или remove_addresses, каждый до 10; одинаковые нормализованные IP не могут добавляться и удаляться одновременно. Это не замена NS домена и не редактирование DNS-зоны.\n\nHTTP 202 подтверждает только сохранение задачи, не успешную EPP-команду. Сохраните data.uuid; проверяйте GET /operations/{operationUuid} (domains.read) либо события domain.operation.updated. pending/processing не окончательны; completed подтверждает выполнение, failed означает окончательный отказ. При uncertain не создавайте новую операцию: сверка запланирована, смотрите next_check_at (обычно +5 минут). Для результата, который нельзя доказать чтением/журналом, потребуется поддержка. Зарезервированные средства удерживаются до подтверждения или окончательного отказа.\n\nОбязателен Idempotency-Key (1..160 символов). Повтор с тем же ключом должен сохранять метод, путь, query и точные байты JSON. HTTP replay обычно хранится 24 часа и возвращает первоначальный ответ, а не актуальную задачу; проверяйте GET /operations/{operationUuid}. Ключ доменной задачи нельзя переиспользовать после истечения HTTP TTL.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.manage"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Задача принята; результат реестра проверяется отдельно.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainOperationEnvelope"
                },
                "examples": {
                  "DomainAcceptedUpdateDomainHost": {
                    "$ref": "#/components/examples/DomainAcceptedUpdateDomainHost"
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Присутствует со значением true при возврате сохранённого HTTP-ответа.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Интеграция домена неактивна, migration hold или есть pending/processing/uncertain/awaiting_registry задача.",
            "recovery": "Дождитесь исходной задачи либо обратитесь к оператору. sync и управление текущим трансфером разрешены при занятой очереди."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Драйвер не поддерживает действие либо домен не зарегистрирован для требующего регистрации действия.",
            "recovery": "Проверьте capabilities и состояние домена; не вызывайте неподдерживаемое действие."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже используется другим запросом или исходный запрос ещё выполняется.",
            "recovery": "Не изменяйте тело при повторе. Проверьте исходную операцию; используйте новый ключ только для нового намеренного действия."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Невалидный JSON-пayload/поле или отсутствует Idempotency-Key.",
            "recovery": "Исправьте данные по error.details; для изменённого запроса создайте новый ключ."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Hostname не является subordinate host текущего домена. Массивы адресов невалидны, превышают 10 элементов или один IP добавляется и удаляется одновременно.",
            "recovery": "Используйте имя вида ns1.example.kz для домена example.kz и корректные адреса; на чужой домен сменой hostname доступ не получить."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body; Content-Type: application/json.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainHostUpdateRequest"
              },
              "examples": {
                "request": {
                  "summary": "Изменить адреса glue-host",
                  "value": {
                    "hostname": "ns1.example.kz",
                    "add_addresses": [
                      "192.0.2.11"
                    ],
                    "remove_addresses": [
                      "192.0.2.10"
                    ],
                    "reason": "Смена IP сервера имён"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request PUT \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/hosts\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-raw '{\n  \"hostname\": \"ns1.example.kz\",\n  \"add_addresses\": [\n    \"192.0.2.11\"\n  ],\n  \"remove_addresses\": [\n    \"192.0.2.10\"\n  ],\n  \"reason\": \"Смена IP сервера имён\"\n}'"
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-async-error-cases": [
          {
            "status": "failed",
            "code": "validation_failed",
            "when": "Обработчик отклонил входные данные/состояние либо получил отказ, классифицированный как ValidationException.",
            "recovery": "Изучите ограничения и журнал через поддержку; новый запрос только после окончательного отказа и устранения причины."
          },
          {
            "status": "failed",
            "code": "registry_rejected",
            "when": "Есть относящийся к этой задаче подтверждённый отказ реестра либо ошибка до отправки.",
            "recovery": "Не повторяйте неизменённую команду; исправьте причину. Резерв освобождается."
          },
          {
            "status": "uncertain",
            "code": "registry_result_unknown",
            "when": "После начала действия нет надёжного подтверждения, произошёл timeout, идентичность ответа не совпала либо сверка пока не установила результат.",
            "recovery": "Не посылайте вторую мутацию. Опрос исходного UUID после next_check_at; при длительном ожидании передайте UUID и request_id поддержке. Резерв не освобождается автоматически."
          }
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ]
      },
      "delete": {
        "tags": [
          "Domains"
        ],
        "summary": "Удалить subordinate glue-host",
        "operationId": "deleteDomainHost",
        "description": "Требуемые scopes: domains.manage.\n\nDELETE требует JSON body и возвращает 202, не 204. Разрешён только subordinate host данного домена. Связанный с делегированием host может быть отклонён реестром; сначала измените зависимые делегирования. Удаление host не удаляет домен.\n\nHTTP 202 подтверждает только сохранение задачи, не успешную EPP-команду. Сохраните data.uuid; проверяйте GET /operations/{operationUuid} (domains.read) либо события domain.operation.updated. pending/processing не окончательны; completed подтверждает выполнение, failed означает окончательный отказ. При uncertain не создавайте новую операцию: сверка запланирована, смотрите next_check_at (обычно +5 минут). Для результата, который нельзя доказать чтением/журналом, потребуется поддержка. Зарезервированные средства удерживаются до подтверждения или окончательного отказа.\n\nОбязателен Idempotency-Key (1..160 символов). Повтор с тем же ключом должен сохранять метод, путь, query и точные байты JSON. HTTP replay обычно хранится 24 часа и возвращает первоначальный ответ, а не актуальную задачу; проверяйте GET /operations/{operationUuid}. Ключ доменной задачи нельзя переиспользовать после истечения HTTP TTL.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.manage"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Задача принята; результат реестра проверяется отдельно.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainOperationEnvelope"
                },
                "examples": {
                  "DomainAcceptedDeleteDomainHost": {
                    "$ref": "#/components/examples/DomainAcceptedDeleteDomainHost"
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Присутствует со значением true при возврате сохранённого HTTP-ответа.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Интеграция домена неактивна, migration hold или есть pending/processing/uncertain/awaiting_registry задача.",
            "recovery": "Дождитесь исходной задачи либо обратитесь к оператору. sync и управление текущим трансфером разрешены при занятой очереди."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Драйвер не поддерживает действие либо домен не зарегистрирован для требующего регистрации действия.",
            "recovery": "Проверьте capabilities и состояние домена; не вызывайте неподдерживаемое действие."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже используется другим запросом или исходный запрос ещё выполняется.",
            "recovery": "Не изменяйте тело при повторе. Проверьте исходную операцию; используйте новый ключ только для нового намеренного действия."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Невалидный JSON-пayload/поле или отсутствует Idempotency-Key.",
            "recovery": "Исправьте данные по error.details; для изменённого запроса создайте новый ключ."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Hostname не является subordinate host текущего домена.",
            "recovery": "Используйте имя вида ns1.example.kz для домена example.kz и корректные адреса; на чужой домен сменой hostname доступ не получить."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body; Content-Type: application/json.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainHostDeleteRequest"
              },
              "examples": {
                "request": {
                  "summary": "Удалить subordinate glue-host",
                  "value": {
                    "hostname": "ns1.example.kz",
                    "reason": "Вывод сервера имён из эксплуатации"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request DELETE \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/hosts\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-raw '{\n  \"hostname\": \"ns1.example.kz\",\n  \"reason\": \"Вывод сервера имён из эксплуатации\"\n}'"
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-async-error-cases": [
          {
            "status": "failed",
            "code": "validation_failed",
            "when": "Обработчик отклонил входные данные/состояние либо получил отказ, классифицированный как ValidationException.",
            "recovery": "Изучите ограничения и журнал через поддержку; новый запрос только после окончательного отказа и устранения причины."
          },
          {
            "status": "failed",
            "code": "registry_rejected",
            "when": "Есть относящийся к этой задаче подтверждённый отказ реестра либо ошибка до отправки.",
            "recovery": "Не повторяйте неизменённую команду; исправьте причину. Резерв освобождается."
          },
          {
            "status": "uncertain",
            "code": "registry_result_unknown",
            "when": "После начала действия нет надёжного подтверждения, произошёл timeout, идентичность ответа не совпала либо сверка пока не установила результат.",
            "recovery": "Не посылайте вторую мутацию. Опрос исходного UUID после next_check_at; при длительном ожидании передайте UUID и request_id поддержке. Резерв не освобождается автоматически."
          }
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ]
      },
      "get": {
        "tags": [
          "Domains"
        ],
        "summary": "Прочитать один subordinate host из реестра",
        "operationId": "getDomainHost",
        "description": "Требуемые scopes: domains.read.\n\nСинхронное чтение одного host по обязательному query hostname; это НЕ список host-объектов. hostname должен находиться внутри текущего домена. Требуются активная интеграция, отсутствие hold и capability host_info. Сырые raw-данные исключены. Обратите внимание: даты createdAt/updatedAt имеют camelCase.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.read"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          },
          {
            "name": "hostname",
            "in": "query",
            "required": true,
            "description": "Полное имя subordinate host; пример ns1.example.kz для домена example.kz.",
            "schema": {
              "$ref": "#/components/schemas/DomainName"
            },
            "example": "ns1.example.kz"
          }
        ],
        "responses": {
          "200": {
            "description": "Успешное чтение или сохранение настройки.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainHostInfoEnvelope"
                },
                "examples": {
                  "DomainHostExists": {
                    "$ref": "#/components/examples/DomainHostExists"
                  },
                  "DomainHostAbsent": {
                    "$ref": "#/components/examples/DomainHostAbsent"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Интеграция неактивна или домен удерживается для миграции.",
            "recovery": "Обратитесь к оператору."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Отсутствует/невалиден hostname, hostname другого домена либо host_info не поддерживается.",
            "recovery": "Исправьте hostname и проверьте capabilities."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Hostname не является subordinate host текущего домена.",
            "recovery": "Используйте имя вида ns1.example.kz для домена example.kz и корректные адреса; на чужой домен сменой hostname доступ не получить."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request GET \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/hosts?hostname=ns1.example.kz\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\""
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ]
      }
    },
    "/domains/transfers": {
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Перенести домен, которого ещё нет в биллинге",
        "operationId": "transferDomainIn",
        "description": "Требуемые scopes: domains.transfer.\n\nСоздаёт pending-услугу и локальный домен, затем одну оплачиваемую transfer-задачу. Предварительно получите quote operation=transfer, quantity=1, year/1; payload.domain_name и четыре contact_uuids обязательны, external_id необязателен. Контакты одного клиента/провайдера/окружения; регламент зоны применяется к владельцу. Для managed обязателен customer_uuid; aggregate использует назначенного клиента. Поддерживается только маршрут transfer_enabled с активным credential нужного окружения. Наличие такого домена у любого провайдера в том же окружении или незавершённое приобретение вызывает 409, не автоматическое присвоение. Для имеющейся записи используйте /domains/{domainUuid}/transfer. Дополнительные scopes до этого шага: contacts.write и quotes.create; для последующего опроса domains.read. Тариф одной операции transfer не гарантирует +1 год, итоговая expires_at берётся из реестра.\n\nНужен provider.checkout_mode=external и действующий одноразовый quote того же API-ключа, окружения, провайдера, клиента и зоны: ровно одна позиция domain_zone с quantity=1. Цену и срок берите из quote, не вычисляйте по цене регистрации. В live резервируется оптовый баланс, после подтверждения резерв списывается; при окончательном отказе освобождается. В test live-баланс не используется.\n\nHTTP 202 подтверждает только сохранение задачи, не успешную EPP-команду. Сохраните data.uuid; проверяйте GET /operations/{operationUuid} (domains.read) либо события domain.operation.updated. pending/processing не окончательны; completed подтверждает выполнение, failed означает окончательный отказ. При uncertain не создавайте новую операцию: сверка запланирована, смотрите next_check_at (обычно +5 минут). Для результата, который нельзя доказать чтением/журналом, потребуется поддержка. Зарезервированные средства удерживаются до подтверждения или окончательного отказа.\n\nОбязателен Idempotency-Key (1..160 символов). Повтор с тем же ключом должен сохранять метод, путь, query и точные байты JSON. HTTP replay обычно хранится 24 часа и возвращает первоначальный ответ, а не актуальную задачу; проверяйте GET /operations/{operationUuid}. Ключ доменной задачи нельзя переиспользовать после истечения HTTP TTL.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.transfer"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Задача принята; результат реестра проверяется отдельно.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainOperationEnvelope"
                },
                "examples": {
                  "DomainAcceptedTransferDomain": {
                    "$ref": "#/components/examples/DomainAcceptedTransferDomain"
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Присутствует со значением true при возврате сохранённого HTTP-ответа.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Интеграция домена неактивна, migration hold или есть pending/processing/uncertain/awaiting_registry задача.",
            "recovery": "Дождитесь исходной задачи либо обратитесь к оператору. sync и управление текущим трансфером разрешены при занятой очереди."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Драйвер не поддерживает действие либо домен не зарегистрирован для требующего регистрации действия.",
            "recovery": "Проверьте capabilities и состояние домена; не вызывайте неподдерживаемое действие."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Домен уже есть в окружении, приобретение выполняется или external_id услуги занят.",
            "recovery": "Найдите существующую запись; не создавайте второй домен. При несовпадении владельца обратитесь к оператору."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже используется другим запросом или исходный запрос ещё выполняется.",
            "recovery": "Не изменяйте тело при повторе. Проверьте исходную операцию; используйте новый ключ только для нового намеренного действия."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Невалидный JSON-пayload/поле или отсутствует Idempotency-Key.",
            "recovery": "Исправьте данные по error.details; для изменённого запроса создайте новый ключ."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body; Content-Type: application/json.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainIncomingTransferRequest"
              },
              "examples": {
                "managed": {
                  "summary": "Перенос для managed-клиента",
                  "value": {
                    "quote_uuid": "019a1234-1000-7000-8000-000000000006",
                    "customer_uuid": "019a1234-1000-7000-8000-000000000002",
                    "auth_code": "DEMO-REPLACE-WITH-CURRENT-REGISTRAR-CODE",
                    "reason": "Перенос домена клиента в наш биллинг"
                  }
                },
                "aggregate": {
                  "summary": "Перенос для aggregate-клиента",
                  "value": {
                    "quote_uuid": "019a1234-1000-7000-8000-000000000006",
                    "auth_code": "DEMO-REPLACE-WITH-CURRENT-REGISTRAR-CODE",
                    "reason": "Перенос агрегированной услуги реселлера"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request POST \"$BASE_URL/domains/transfers\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-raw '{\n  \"quote_uuid\": \"019a1234-1000-7000-8000-000000000006\",\n  \"customer_uuid\": \"019a1234-1000-7000-8000-000000000002\",\n  \"auth_code\": \"DEMO-REPLACE-WITH-CURRENT-REGISTRAR-CODE\",\n  \"reason\": \"Перенос домена клиента в наш биллинг\"\n}'"
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-prerequisite-scopes": [
          {
            "scope": "quotes.create",
            "when": "Для выпуска quote до вызова этого endpoint."
          },
          {
            "scope": "contacts.write",
            "when": "Если контактные профили ещё не созданы."
          },
          {
            "scope": "domains.read",
            "when": "Для последующего чтения домена/операции."
          }
        ],
        "x-async-error-cases": [
          {
            "status": "failed",
            "code": "validation_failed",
            "when": "Обработчик отклонил входные данные/состояние либо получил отказ, классифицированный как ValidationException.",
            "recovery": "Изучите ограничения и журнал через поддержку; новый запрос только после окончательного отказа и устранения причины."
          },
          {
            "status": "failed",
            "code": "registry_rejected",
            "when": "Есть относящийся к этой задаче подтверждённый отказ реестра либо ошибка до отправки.",
            "recovery": "Не повторяйте неизменённую команду; исправьте причину. Резерв освобождается."
          },
          {
            "status": "uncertain",
            "code": "registry_result_unknown",
            "when": "После начала действия нет надёжного подтверждения, произошёл timeout, идентичность ответа не совпала либо сверка пока не установила результат.",
            "recovery": "Не посылайте вторую мутацию. Опрос исходного UUID после next_check_at; при длительном ожидании передайте UUID и request_id поддержке. Резерв не освобождается автоматически."
          },
          {
            "status": "failed",
            "code": "transfer_rejected",
            "when": "Сверка исходного transfer увидела rejected/canceled.",
            "recovery": "Резерв освобождается; выясните причину у текущего регистратора."
          }
        ],
        "x-prerequisite-request-example": {
          "description": "Предварительный запрос POST /quotes (другой endpoint, scope quotes.create, отдельный Idempotency-Key). UUID из data.uuid полученного quote передайте в quote_uuid текущей операции. Не используйте здесь демонстрационные UUID или имя без замены.",
          "method": "POST",
          "path": "/quotes",
          "body": {
            "customer_uuid": "019a1234-1000-7000-8000-000000000002",
            "items": [
              {
                "resource_type": "domain_zone",
                "resource_uuid": "019a1234-1000-7000-8000-000000000004",
                "operation": "transfer",
                "period_unit": "year",
                "period_count": 1,
                "quantity": 1,
                "currency": "KZT",
                "payload": {
                  "domain_name": "example.kz",
                  "contact_uuids": {
                    "owner": "019a1234-1000-7000-8000-000000000003",
                    "admin": "019a1234-1000-7000-8000-000000000003",
                    "tech": "019a1234-1000-7000-8000-000000000003",
                    "billing": "019a1234-1000-7000-8000-000000000003"
                  },
                  "external_id": "crm-domain-1001"
                }
              }
            ]
          }
        }
      }
    },
    "/domains/{domainUuid}/capabilities": {
      "get": {
        "tags": [
          "Domains"
        ],
        "summary": "Проверить поддерживаемые операции",
        "operationId": "getDomainCapabilities",
        "description": "Требуемые scopes: domains.read.\n\nОтвет зависит от интерфейсов драйвера, active integration credential и migration hold, но НЕ учитывает scopes ключа, баланс и все EPP-ограничения. true не обещает успешное действие. DNSSEC всегда false; методов управления DNSSEC или A/MX/TXT здесь нет. renewal_available_at отражает только первые 24 часа, maximum_term_years ограничивает оставшийся срок; дополнительные запреты смотрите в описании renew.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.read"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          }
        ],
        "responses": {
          "200": {
            "description": "Успешное чтение или сохранение настройки.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainCapabilitiesEnvelope"
                },
                "examples": {
                  "DomainCapabilities": {
                    "$ref": "#/components/examples/DomainCapabilities"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request GET \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/capabilities\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\""
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ]
      }
    },
    "/domains/{domainUuid}/auto-renew": {
      "put": {
        "tags": [
          "Domains"
        ],
        "summary": "Включить или выключить автопродление",
        "operationId": "setDomainAutoRenew",
        "description": "Требуемые scopes: domains.manage.\n\nСинхронное сохранение настройки: HTTP 200, не задача EPP. При enabled=true ДОПОЛНИТЕЛЬНО нужен domains.renew. Только external checkout; активная интеграция и отсутствие migration hold. Согласие относится к текущему ключу. Планировщик рассматривает домен ежечасно в окне 7 дней до окончания и создаёт продление на 1 год по АКТУАЛЬНОМУ тарифу; цена не фиксируется в момент включения. Применяются ограничения 24 часа/10 лет, баланс и реестровые запреты. Отозванный ключ, отсутствующая цена или нехватка средств не гарантируют продление; следите за domain.auto_renew.failed и domain.operation.updated. Отключение не отменяет уже принятую задачу. Retail auto-invoices не включаются; test не использует live-средства.\n\nОбязателен Idempotency-Key (1..160 символов). Повтор с тем же ключом должен сохранять метод, путь, query и точные байты JSON. HTTP replay обычно хранится 24 часа и возвращает первоначальный ответ, а не актуальную задачу; проверяйте GET /operations/{operationUuid}. Ключ доменной задачи нельзя переиспользовать после истечения HTTP TTL.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.manage"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Успешное чтение или сохранение настройки.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainAutoRenewEnvelope"
                },
                "examples": {
                  "DomainAutoRenewEnabled": {
                    "$ref": "#/components/examples/DomainAutoRenewEnabled"
                  },
                  "DomainAutoRenewDisabled": {
                    "$ref": "#/components/examples/DomainAutoRenewDisabled"
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Присутствует со значением true при возврате сохранённого HTTP-ответа.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "enabled=true без domains.renew.",
            "recovery": "Добавьте domains.renew либо отправьте enabled=false."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Неактивная интеграция или migration hold.",
            "recovery": "Устраните ограничение через оператора."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Checkout провайдера не external.",
            "recovery": "Эта настройка недоступна в platform checkout."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже используется другим запросом или исходный запрос ещё выполняется.",
            "recovery": "Не изменяйте тело при повторе. Проверьте исходную операцию; используйте новый ключ только для нового намеренного действия."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Невалидный JSON-пayload/поле или отсутствует Idempotency-Key.",
            "recovery": "Исправьте данные по error.details; для изменённого запроса создайте новый ключ."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body; Content-Type: application/json.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainAutoRenewRequest"
              },
              "examples": {
                "enable": {
                  "summary": "Согласие на актуальную цену",
                  "value": {
                    "enabled": true,
                    "reason": "Клиент согласовал продление по текущему тарифу"
                  }
                },
                "disable": {
                  "summary": "Отключение будущих продлений",
                  "value": {
                    "enabled": false,
                    "reason": "Клиент отключил автоматическое продление"
                  }
                }
              }
            }
          }
        },
        "x-additional-scopes": [
          {
            "scope": "domains.renew",
            "when": "enabled=true"
          }
        ],
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request PUT \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/auto-renew\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-raw '{\n  \"enabled\": true,\n  \"reason\": \"Клиент согласовал продление по текущему тарифу\"\n}'"
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ]
      }
    },
    "/operations/{operationUuid}": {
      "get": {
        "tags": [
          "Operations"
        ],
        "summary": "Прочитать состояние доменной операции",
        "operationId": "getDomainOperation",
        "description": "Требуемые scopes: domains.read.\n\nТолько reseller_domain_operation своего провайдера и окружения. register_domain здесь отсутствует: регистрацию отслеживайте через Order/Service/Domain. HTTP 200 возможен при любом прикладном статусе, включая failed и uncertain. result обычно [] до completed. result.auth_code раскрывается только для GET при domains.auth-code; без него поле отсутствует. Не кэшируйте/не логируйте секреты. Polling с backoff/jitter, учитывайте next_check_at, а не каждую секунду. failed окончателен; ошибку анализируйте в data.error, не как HTTP ErrorEnvelope.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.read"
        ],
        "parameters": [
          {
            "name": "operationUuid",
            "in": "path",
            "required": true,
            "description": "UUID из ответа доменной операции, не UUID заказа или register_domain.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "019a1234-1000-7000-8000-000000000007"
          }
        ],
        "responses": {
          "200": {
            "description": "Успешное чтение или сохранение настройки.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainOperationEnvelope"
                },
                "examples": {
                  "DomainOperationPending": {
                    "$ref": "#/components/examples/DomainOperationPending"
                  },
                  "DomainOperationProcessing": {
                    "$ref": "#/components/examples/DomainOperationProcessing"
                  },
                  "DomainOperationCompleted": {
                    "$ref": "#/components/examples/DomainOperationCompleted"
                  },
                  "DomainOperationFailed": {
                    "$ref": "#/components/examples/DomainOperationFailed"
                  },
                  "DomainOperationUncertain": {
                    "$ref": "#/components/examples/DomainOperationUncertain"
                  },
                  "DomainOperationAuthCode": {
                    "$ref": "#/components/examples/DomainOperationAuthCode"
                  },
                  "DomainOperationAuthCodeRedacted": {
                    "$ref": "#/components/examples/DomainOperationAuthCodeRedacted"
                  }
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "description": "Запрет хранения ответа.",
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          }
        ],
        "x-additional-scopes": [
          {
            "scope": "domains.auth-code",
            "when": "Для чтения result.auth_code."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request GET \"$BASE_URL/operations/019a1234-1000-7000-8000-000000000007\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\""
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ]
      }
    },
    "/domains/{domainUuid}/operations": {
      "get": {
        "tags": [
          "Operations"
        ],
        "summary": "История доменных операций",
        "operationId": "listDomainOperations",
        "description": "Требуемые scopes: domains.read.\n\nПагинация id по убыванию, только reseller_domain_operation; без задач регистрации/клиентского renewal. Доступна для soft-deleted доменов того же провайдера/окружения. Ответ отличается от /domains: только data и meta.next_cursor, без links/path/prev_cursor. При дополнительном domains.auth-code результаты могут содержать auth-коды; исключите тело из логирования.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.read"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          }
        ],
        "responses": {
          "200": {
            "description": "Успешное чтение или сохранение настройки.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainOperationListEnvelope"
                },
                "examples": {
                  "DomainOperationList": {
                    "$ref": "#/components/examples/DomainOperationList"
                  }
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "description": "Запрет хранения ответа.",
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          }
        ],
        "x-additional-scopes": [
          {
            "scope": "domains.auth-code",
            "when": "Для чтения result.auth_code."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request GET \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/operations\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\""
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainOperationController.php"
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ]
      }
    },
    "/domains/{domainUuid}/registrant-verification/payload": {
      "get": {
        "tags": [
          "Domains"
        ],
        "summary": "Получить точный документ для ЭЦП владельца",
        "operationId": "getRegistrantVerificationPayload",
        "description": "Требуемые scopes: domains.verify.\n\nДомен должен быть создан в реестре; нужны активная интеграция и отсутствие migration hold. Если последняя принятая подпись pending или владелец уже verified, новая подготовка блокируется 422. Данные владельца могут запрашиваться у NIC.KZ. Подписывайте ТОЧНЫЕ UTF-8 байты signable_payload через NCALayer с возвращёнными параметрами; не собирайте документ самостоятельно и не подписывайте пример. payload_hash = SHA-256 текста. expires_at = issued_at+15 минут рекомендует обновить документ; submit не принимает отдельный подписанный token срока, а проверяет актуальный snapshot и точный текст. Ответ содержит идентификатор владельца, не сохраняйте его в публичных логах. Это не общая доменная Operation.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.verify"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          }
        ],
        "responses": {
          "200": {
            "description": "Успешное чтение или сохранение настройки.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainVerificationPayloadEnvelope"
                },
                "examples": {
                  "DomainVerificationPayload": {
                    "$ref": "#/components/examples/DomainVerificationPayload"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Интеграция неактивна или migration hold.",
            "recovery": "Обратитесь к оператору."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Домен не создан в реестре, последняя проверка pending или уже verified.",
            "recovery": "Дождитесь завершения регистрации/верификации; повторная подпись не нужна."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request GET \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/registrant-verification/payload\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\""
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainController.php"
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ]
      }
    },
    "/domains/{domainUuid}/registrant-verification": {
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Отправить ЭЦП и создать попытку проверки владельца",
        "operationId": "submitRegistrantVerification",
        "description": "Требуемые scopes: domains.verify.\n\nСначала получите свежий payload. Передайте payload_snapshot, точный signable_payload и signature_options без изменений, signature = Base64 DER detached CMS CAdES-T с меткой времени. Проверяются криптография, доверие, отзыв и соответствие ИИН/БИН/полномочий владельцу; специальная политика ИП для .edu.kz не отменяет криптопроверку. Невалидный тип подписи, CMS, mismatch документа или отказ регистратора может создать Verification со status=failed и HTTP 201, а не HTTP 422. HTTP 422 означает ошибку HTTP-валидации либо запрет новой попытки (pending/already verified/конкурентная обработка). Проверяйте data.status и failure_message; отдельный failure_code ресурс не выдаёт. Для pending ждите реестр, новую подпись не отправляйте; если ожидание затянулось, обращайтесь в поддержку. Отказ по образовательной лицензии независим от действительности ЭЦП. JSON-пример полный, но содержит демонстрационный идентификатор и НЕ настоящую подпись; подпись всегда формируется на устройстве владельца по свежему документу.\n\nОбязателен Idempotency-Key (1..160 символов). Повтор с тем же ключом должен сохранять метод, путь, query и точные байты JSON. HTTP replay обычно хранится 24 часа и возвращает первоначальный ответ, а не актуальную задачу; проверяйте GET /operations/{operationUuid}. Ключ доменной задачи нельзя переиспользовать после истечения HTTP TTL.\n\nЛимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "domains.verify"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "201": {
            "description": "Запись создана; проверяйте прикладной status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainVerificationEnvelope"
                },
                "examples": {
                  "DomainVerificationPending": {
                    "$ref": "#/components/examples/DomainVerificationPending"
                  },
                  "DomainVerificationVerified": {
                    "$ref": "#/components/examples/DomainVerificationVerified"
                  },
                  "DomainVerificationFailed": {
                    "$ref": "#/components/examples/DomainVerificationFailed"
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Присутствует со значением true при возврате сохранённого HTTP-ответа.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет действительного Bearer API-ключа.",
            "recovery": "Проверьте ключ, срок действия и окружение; не повторяйте бесконечно."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет требуемого scope, провайдер/ключ/доступ запрещён.",
            "recovery": "Проверьте настройки провайдера и scopes ключа."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпан общий лимит ключа.",
            "recovery": "Выждите Retry-After; повторы выполняйте с backoff и jitter."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Необработанный сбой инфраструктуры или внешнего драйвера.",
            "recovery": "Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold.",
            "recovery": "Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Интеграция неактивна или migration hold.",
            "recovery": "Обратитесь к оператору."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Не хватает полей; type=eds без signature/options; уже pending/verified или конкурентная обработка.",
            "recovery": "Исправьте формат либо дождитесь текущей попытки. Новый Idempotency-Key не снимает блокировку pending."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже используется другим запросом или исходный запрос ещё выполняется.",
            "recovery": "Не изменяйте тело при повторе. Проверьте исходную операцию; используйте новый ключ только для нового намеренного действия."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Невалидный JSON-пayload/поле или отсутствует Idempotency-Key.",
            "recovery": "Исправьте данные по error.details; для изменённого запроса создайте новый ключ."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "Reseller API глобально выключен конфигурацией.",
            "recovery": "Уточните base URL и включение API у оператора."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body; Content-Type: application/json.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainVerificationRequest"
              },
              "examples": {
                "DomainVerificationSubmission": {
                  "$ref": "#/components/examples/DomainVerificationSubmission"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.\n# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.\ncurl --request POST \"$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/registrant-verification\" \\\n  --header \"Authorization: Bearer $RESELLER_API_TOKEN\" \\\n  --header \"Accept: application/json\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-raw '{\n  \"type\": \"eds\",\n  \"payload_snapshot\": {\n    \"domain-name\": \"example.kz\",\n    \"domain-creation-time\": \"2026-10-01T08:00:00.000Z\",\n    \"registrant-name\": \"Ivan Petrov\",\n    \"registrant-org\": null,\n    \"registrant-residencedetails-country\": \"KZ\",\n    \"registrant-residencedetails-externalidtype\": \"IIN\",\n    \"registrant-residencedetails-externalidvalue\": \"000000000000\"\n  },\n  \"signable_payload\": \"[KK] ТІРКЕУШІНІҢ ТІРКЕУ ДЕРЕКТЕРІН РАСТАУ ТУРАЛЫ ӨТІНІШ\\n\\nОсы өтініш арқылы мен көрсетілген домендік атаудың тіркеу деректерінің өзектілігі мен дұрыстығын Қазақстан интернет\\nсегментіндегі домендік атауларды тіркеу, пайдалану және бөлу қағидаларына сәйкес толық растаймын.\\n\\nОПЕРАЦИЯ ТУРАЛЫ ДЕРЕКТЕР:\\n---------------------------------------------------------------------------\\n* Домендік атау: example.kz\\n* Құрылған күні: 2026-10-01T08:00:00.000Z\\n* Тіркеуші (Т.А.Ә.): Ivan Petrov\\n* Ұйым: \\n* Ел: KZ\\n* Құжат түрі: IIN\\n* Құжат нөмірі: 000000000000\\n\\nБұл құжат Қазақстандық торап ақпарат орталығына (KazNIC) жіберуге арналған.\\n\\nKazNIC. Барлық құқықтар қорғалған.\\n\\n\\n[RU] ЗАЯВЛЕНИЕ НА ПОДТВЕРЖДЕНИЕ РЕГИСТРАЦИОННЫХ ДАННЫХ\\n\\nНастоящим действием я в полной мере подтверждаю актуальность и достоверность регистрационных данных указанного\\nдоменного имени в соответствии с Правилами регистрации, пользования и распределения доменных имен в пространстве\\nказахстанского сегмента Интернета.\\n\\nДАННЫЕ ОПЕРАЦИИ:\\n---------------------------------------------------------------------------\\n* Доменное имя: example.kz\\n* Дата создания: 2026-10-01T08:00:00.000Z\\n* Регистрант (ФИО): Ivan Petrov\\n* Организация: \\n* Страна: KZ\\n* Тип документа: IIN\\n* Номер документа: 000000000000\\n\\nДанный документ предназначен для отправки в Казахстанский центр сетевой информации (KazNIC).\\n\\nKazNIC. Все права защищены.\",\n  \"signature\": \"REPLACE_WITH_BASE64_DER_CADES_T_FROM_NCALAYER\",\n  \"certificate\": null,\n  \"eds_provider\": \"ncalayer\",\n  \"signature_options\": {\n    \"method\": \"kz.gov.pki.knca.basics.sign\",\n    \"format\": \"cms\",\n    \"decode\": false,\n    \"encapsulate\": false,\n    \"digested\": false,\n    \"timestamp_applied\": true,\n    \"cms_type\": \"CMS Detached\",\n    \"cades_profile\": \"CAdES-T\"\n  }\n}'"
          }
        ],
        "x-implementation-sources": [
          "routes/api/reseller/v1.php",
          "app/Application/ResellerApi/V1/Controller/DomainController.php"
        ],
        "x-input-caveats": [
          "Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
        ]
      }
    },
    "/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "listWebhooks",
        "summary": "Список подписок",
        "description": "Порядок от новых к старым, cursor pagination. Только текущая среда.\n\nScope: webhooks.read. Bearer-ключ определяет реселлера и среду; подписки не принадлежат одному конкретному API-ключу. Общие правила ошибок и повторов см. руководство надёжности.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "webhooks.read"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookList"
                },
                "examples": {
                  "default": {
                    "value": {
                      "data": [
                        {
                          "uuid": "22222222-2222-4222-8222-222222222222",
                          "environment": "test",
                          "name": "Domain events",
                          "url": "https://reseller.example/webhooks/billing",
                          "event_subscriptions": [
                            "domain.operation.updated",
                            "domain.registration.updated",
                            "domain.verification.updated"
                          ],
                          "timeout_seconds": 10,
                          "max_attempts": 8,
                          "status": "active",
                          "rotated_at": null,
                          "disabled_at": null,
                          "created_at": "2026-10-05T09:00:00+00:00"
                        }
                      ],
                      "meta": {
                        "next_cursor": null,
                        "per_page": 25
                      }
                    }
                  },
                  "empty": {
                    "value": {
                      "data": [],
                      "meta": {
                        "next_cursor": null,
                        "per_page": 25
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет корректного активного Bearer-ключа либо он истёк/отозван.",
            "recovery": "Получите/исправьте ключ; не повторяйте без изменения причины."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет scope операции, IP запрещён, API не включён для реселлера или его статус/тариф запрещает запрос.",
            "recovery": "Сверьте GET /context и разрешения у провайдера."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "API отключён; для UUID-маршрута также ресурс отсутствует, чужой или другой среды.",
            "recovery": "Проверьте UUID и среду ключа; не перебирайте чужие ресурсы."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпана общая квота этого API-ключа.",
            "recovery": "Ждите Retry-After секунд плюс jitter; запись повторяйте с тем же Idempotency-Key."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Внутренний сбой; некорректный UUID также может не пройти обработку как 422.",
            "recovery": "Сохраните X-Request-ID, передавайте валидный UUID. После записи сначала сверка состояния, не новая операция."
          }
        ]
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "createWebhook",
        "summary": "Создать подписку",
        "description": "Подписка создаётся active. URL проверяется сейчас и перед каждой доставкой; создание не выполняет HTTP test. Секрет выдаётся в ответе, а не GET. Повтор того же HTTP-запроса в пределах TTL может вернуть сохранённый секрет.\n\nScope: webhooks.manage. Bearer-ключ определяет реселлера и среду; подписки не принадлежат одному конкретному API-ключу. Общие правила ошибок и повторов см. руководство надёжности.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "webhooks.manage"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "201": {
            "description": "Успешный ответ.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/IdempotencyReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSecretResponse"
                },
                "examples": {
                  "default": {
                    "value": {
                      "data": {
                        "uuid": "22222222-2222-4222-8222-222222222222",
                        "environment": "test",
                        "name": "Domain events",
                        "url": "https://reseller.example/webhooks/billing",
                        "event_subscriptions": [
                          "domain.operation.updated",
                          "domain.registration.updated",
                          "domain.verification.updated"
                        ],
                        "timeout_seconds": 10,
                        "max_attempts": 8,
                        "status": "active",
                        "rotated_at": null,
                        "disabled_at": null,
                        "created_at": "2026-10-05T09:00:00+00:00"
                      },
                      "signing_secret": "whsec_EXAMPLE0EXAMPLE0EXAMPLE0EXAMPLE0EXAMPLE0EXAMPLE0EXAMPLE0EXAMPLE0",
                      "secret_notice": "This signing secret is shown once and cannot be recovered."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет корректного активного Bearer-ключа либо он истёк/отозван.",
            "recovery": "Получите/исправьте ключ; не повторяйте без изменения причины."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет scope операции, IP запрещён, API не включён для реселлера или его статус/тариф запрещает запрос.",
            "recovery": "Сверьте GET /context и разрешения у провайдера."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "API отключён; для UUID-маршрута также ресурс отсутствует, чужой или другой среды.",
            "recovery": "Проверьте UUID и среду ключа; не перебирайте чужие ресурсы."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпана общая квота этого API-ключа.",
            "recovery": "Ждите Retry-After секунд плюс jitter; запись повторяйте с тем же Idempotency-Key."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Внутренний сбой; некорректный UUID также может не пройти обработку как 422.",
            "recovery": "Сохраните X-Request-ID, передавайте валидный UUID. После записи сначала сверка состояния, не новая операция."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже обрабатывается или ранее использован с другим method/path/query/body.",
            "recovery": "Не меняйте ключ после неизвестного результата. Дождитесь исходной попытки; исправленный запрос оформляйте новым ключом только после сверки."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Idempotency-Key отсутствует/пуст/длиннее 160 символов.",
            "recovery": "Передайте уникальный ключ на логическую операцию."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Неверные name/url/events/timeout/max_attempts/status; URL не публичный HTTPS, DNS не разрешается или есть запрещённый адрес.",
            "recovery": "Исправьте поля из details, DNS и сертификат; запрос с исправленным телом отправляйте с новым ключом."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreate"
              },
              "examples": {
                "default": {
                  "value": {
                    "name": "Domain events",
                    "url": "https://reseller.example/webhooks/billing",
                    "event_subscriptions": [
                      "domain.operation.updated",
                      "domain.registration.updated",
                      "domain.verification.updated"
                    ],
                    "timeout_seconds": 10,
                    "max_attempts": 8
                  }
                }
              }
            }
          }
        }
      }
    },
    "/webhooks/{webhookUuid}": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "getWebhook",
        "summary": "Получить подписку",
        "description": "Возвращает настройки, но не секрет.\n\nScope: webhooks.read. Bearer-ключ определяет реселлера и среду; подписки не принадлежат одному конкретному API-ключу. Общие правила ошибок и повторов см. руководство надёжности.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "webhooks.read"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookUuid"
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookResponse"
                },
                "examples": {
                  "default": {
                    "value": {
                      "data": {
                        "uuid": "22222222-2222-4222-8222-222222222222",
                        "environment": "test",
                        "name": "Domain events",
                        "url": "https://reseller.example/webhooks/billing",
                        "event_subscriptions": [
                          "domain.operation.updated",
                          "domain.registration.updated",
                          "domain.verification.updated"
                        ],
                        "timeout_seconds": 10,
                        "max_attempts": 8,
                        "status": "active",
                        "rotated_at": null,
                        "disabled_at": null,
                        "created_at": "2026-10-05T09:00:00+00:00"
                      }
                    }
                  },
                  "disabled": {
                    "value": {
                      "data": {
                        "uuid": "22222222-2222-4222-8222-222222222222",
                        "environment": "test",
                        "name": "Domain events",
                        "url": "https://reseller.example/webhooks/billing",
                        "event_subscriptions": [
                          "domain.operation.updated",
                          "domain.registration.updated",
                          "domain.verification.updated"
                        ],
                        "timeout_seconds": 10,
                        "max_attempts": 8,
                        "status": "disabled",
                        "rotated_at": null,
                        "disabled_at": "2026-10-05T10:00:00+00:00",
                        "created_at": "2026-10-05T09:00:00+00:00"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет корректного активного Bearer-ключа либо он истёк/отозван.",
            "recovery": "Получите/исправьте ключ; не повторяйте без изменения причины."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет scope операции, IP запрещён, API не включён для реселлера или его статус/тариф запрещает запрос.",
            "recovery": "Сверьте GET /context и разрешения у провайдера."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "API отключён; для UUID-маршрута также ресурс отсутствует, чужой или другой среды.",
            "recovery": "Проверьте UUID и среду ключа; не перебирайте чужие ресурсы."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпана общая квота этого API-ключа.",
            "recovery": "Ждите Retry-After секунд плюс jitter; запись повторяйте с тем же Idempotency-Key."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Внутренний сбой; некорректный UUID также может не пройти обработку как 422.",
            "recovery": "Сохраните X-Request-ID, передавайте валидный UUID. После записи сначала сверка состояния, не новая операция."
          }
        ]
      },
      "patch": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "updateWebhook",
        "summary": "Изменить подписку",
        "description": "Непереданные поля сохраняются. status=disabled при неизменном URL позволяет отключить получателя даже с недоступным DNS. Включение и остальные изменения перепроверяют URL. Очередные доставки используют актуальные URL/секрет/timeout/max_attempts; изменение подписок не отменяет уже созданные доставки.\n\nScope: webhooks.manage. Bearer-ключ определяет реселлера и среду; подписки не принадлежат одному конкретному API-ключу. Общие правила ошибок и повторов см. руководство надёжности.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "webhooks.manage"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/IdempotencyReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookResponse"
                },
                "examples": {
                  "default": {
                    "value": {
                      "data": {
                        "uuid": "22222222-2222-4222-8222-222222222222",
                        "environment": "test",
                        "name": "Domain events",
                        "url": "https://reseller.example/webhooks/billing",
                        "event_subscriptions": [
                          "domain.operation.updated",
                          "domain.registration.updated",
                          "domain.verification.updated"
                        ],
                        "timeout_seconds": 10,
                        "max_attempts": 8,
                        "status": "disabled",
                        "rotated_at": null,
                        "disabled_at": "2026-10-05T10:00:00+00:00",
                        "created_at": "2026-10-05T09:00:00+00:00"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет корректного активного Bearer-ключа либо он истёк/отозван.",
            "recovery": "Получите/исправьте ключ; не повторяйте без изменения причины."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет scope операции, IP запрещён, API не включён для реселлера или его статус/тариф запрещает запрос.",
            "recovery": "Сверьте GET /context и разрешения у провайдера."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "API отключён; для UUID-маршрута также ресурс отсутствует, чужой или другой среды.",
            "recovery": "Проверьте UUID и среду ключа; не перебирайте чужие ресурсы."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпана общая квота этого API-ключа.",
            "recovery": "Ждите Retry-After секунд плюс jitter; запись повторяйте с тем же Idempotency-Key."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Внутренний сбой; некорректный UUID также может не пройти обработку как 422.",
            "recovery": "Сохраните X-Request-ID, передавайте валидный UUID. После записи сначала сверка состояния, не новая операция."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже обрабатывается или ранее использован с другим method/path/query/body.",
            "recovery": "Не меняйте ключ после неизвестного результата. Дождитесь исходной попытки; исправленный запрос оформляйте новым ключом только после сверки."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Idempotency-Key отсутствует/пуст/длиннее 160 символов.",
            "recovery": "Передайте уникальный ключ на логическую операцию."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Неверные name/url/events/timeout/max_attempts/status; URL не публичный HTTPS, DNS не разрешается или есть запрещённый адрес.",
            "recovery": "Исправьте поля из details, DNS и сертификат; запрос с исправленным телом отправляйте с новым ключом."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookUpdate"
              },
              "examples": {
                "default": {
                  "value": {
                    "status": "disabled"
                  }
                },
                "enable": {
                  "value": {
                    "status": "active"
                  }
                },
                "change": {
                  "value": {
                    "name": "Renewal monitor",
                    "url": "https://reseller.example/webhooks/domains",
                    "event_subscriptions": [
                      "domain.operation.updated"
                    ],
                    "timeout_seconds": 15,
                    "max_attempts": 10
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "deleteWebhook",
        "summary": "Удалить подписку",
        "description": "Удаляет подписку и связанные записи доставок. Не удаляет домены и не отменяет доменные операции. Текущая сетевая попытка может уже выполняться. Повтор с тем же HTTP-ключом может вернуть сохранённый 204, с новым ключом после удаления ожидайте 404.\n\nScope: webhooks.manage. Bearer-ключ определяет реселлера и среду; подписки не принадлежат одному конкретному API-ключу. Общие правила ошибок и повторов см. руководство надёжности.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "webhooks.manage"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "Удалено. Тело отсутствует; не пытайтесь декодировать JSON.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/IdempotencyReplayed"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет корректного активного Bearer-ключа либо он истёк/отозван.",
            "recovery": "Получите/исправьте ключ; не повторяйте без изменения причины."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет scope операции, IP запрещён, API не включён для реселлера или его статус/тариф запрещает запрос.",
            "recovery": "Сверьте GET /context и разрешения у провайдера."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "API отключён; для UUID-маршрута также ресурс отсутствует, чужой или другой среды.",
            "recovery": "Проверьте UUID и среду ключа; не перебирайте чужие ресурсы."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпана общая квота этого API-ключа.",
            "recovery": "Ждите Retry-After секунд плюс jitter; запись повторяйте с тем же Idempotency-Key."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Внутренний сбой; некорректный UUID также может не пройти обработку как 422.",
            "recovery": "Сохраните X-Request-ID, передавайте валидный UUID. После записи сначала сверка состояния, не новая операция."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже обрабатывается или ранее использован с другим method/path/query/body.",
            "recovery": "Не меняйте ключ после неизвестного результата. Дождитесь исходной попытки; исправленный запрос оформляйте новым ключом только после сверки."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Idempotency-Key отсутствует/пуст/длиннее 160 символов.",
            "recovery": "Передайте уникальный ключ на логическую операцию."
          }
        ]
      }
    },
    "/webhooks/{webhookUuid}/rotate-secret": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "rotateWebhookSecret",
        "summary": "Заменить секрет подписи",
        "description": "Новый секрет действует сразу, старый на стороне отправителя не сохраняется. Отложенные повторы подписываются новым; уже отправленный запрос может иметь старую подпись. GET секрет не выдаёт; HTTP replay может выдать сохранённый ответ. Ротация не включает disabled-подписку.\n\nScope: webhooks.manage. Bearer-ключ определяет реселлера и среду; подписки не принадлежат одному конкретному API-ключу. Общие правила ошибок и повторов см. руководство надёжности.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "webhooks.manage"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/IdempotencyReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSecretResponse"
                },
                "examples": {
                  "default": {
                    "value": {
                      "data": {
                        "uuid": "22222222-2222-4222-8222-222222222222",
                        "environment": "test",
                        "name": "Domain events",
                        "url": "https://reseller.example/webhooks/billing",
                        "event_subscriptions": [
                          "domain.operation.updated",
                          "domain.registration.updated",
                          "domain.verification.updated"
                        ],
                        "timeout_seconds": 10,
                        "max_attempts": 8,
                        "status": "active",
                        "rotated_at": "2026-10-05T10:00:00+00:00",
                        "disabled_at": null,
                        "created_at": "2026-10-05T09:00:00+00:00"
                      },
                      "signing_secret": "whsec_EXAMPLE0EXAMPLE0EXAMPLE0EXAMPLE0EXAMPLE0EXAMPLE0EXAMPLE0EXAMPLE0",
                      "secret_notice": "This signing secret is shown once and cannot be recovered."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет корректного активного Bearer-ключа либо он истёк/отозван.",
            "recovery": "Получите/исправьте ключ; не повторяйте без изменения причины."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет scope операции, IP запрещён, API не включён для реселлера или его статус/тариф запрещает запрос.",
            "recovery": "Сверьте GET /context и разрешения у провайдера."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "API отключён; для UUID-маршрута также ресурс отсутствует, чужой или другой среды.",
            "recovery": "Проверьте UUID и среду ключа; не перебирайте чужие ресурсы."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпана общая квота этого API-ключа.",
            "recovery": "Ждите Retry-After секунд плюс jitter; запись повторяйте с тем же Idempotency-Key."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Внутренний сбой; некорректный UUID также может не пройти обработку как 422.",
            "recovery": "Сохраните X-Request-ID, передавайте валидный UUID. После записи сначала сверка состояния, не новая операция."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже обрабатывается или ранее использован с другим method/path/query/body.",
            "recovery": "Не меняйте ключ после неизвестного результата. Дождитесь исходной попытки; исправленный запрос оформляйте новым ключом только после сверки."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Idempotency-Key отсутствует/пуст/длиннее 160 символов.",
            "recovery": "Передайте уникальный ключ на логическую операцию."
          }
        ]
      }
    },
    "/webhooks/{webhookUuid}/test": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "testWebhook",
        "summary": "Отправить тестовое событие",
        "description": "Создаёт событие webhook.test только для выбранной active-подписки, независимо от event_subscriptions. 202 означает очередь; прочитайте deliveries для подтверждения. Тело запроса не требуется.\n\nScope: webhooks.manage. Bearer-ключ определяет реселлера и среду; подписки не принадлежат одному конкретному API-ключу. Общие правила ошибок и повторов см. руководство надёжности.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "webhooks.manage"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Успешный ответ.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/IdempotencyReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeliveryResponse"
                },
                "examples": {
                  "default": {
                    "value": {
                      "data": {
                        "uuid": "33333333-3333-4333-8333-333333333333",
                        "event_uuid": "44444444-4444-4444-8444-444444444444",
                        "event_type": "webhook.test",
                        "status": "pending",
                        "attempt_number": 0,
                        "http_status": null,
                        "error_code": null,
                        "next_attempt_at": "2026-10-05T09:01:00+00:00",
                        "delivered_at": null,
                        "failed_at": null
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет корректного активного Bearer-ключа либо он истёк/отозван.",
            "recovery": "Получите/исправьте ключ; не повторяйте без изменения причины."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет scope операции, IP запрещён, API не включён для реселлера или его статус/тариф запрещает запрос.",
            "recovery": "Сверьте GET /context и разрешения у провайдера."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "API отключён; для UUID-маршрута также ресурс отсутствует, чужой или другой среды.",
            "recovery": "Проверьте UUID и среду ключа; не перебирайте чужие ресурсы."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпана общая квота этого API-ключа.",
            "recovery": "Ждите Retry-After секунд плюс jitter; запись повторяйте с тем же Idempotency-Key."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Внутренний сбой; некорректный UUID также может не пройти обработку как 422.",
            "recovery": "Сохраните X-Request-ID, передавайте валидный UUID. После записи сначала сверка состояния, не новая операция."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже обрабатывается или ранее использован с другим method/path/query/body.",
            "recovery": "Не меняйте ключ после неизвестного результата. Дождитесь исходной попытки; исправленный запрос оформляйте новым ключом только после сверки."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Idempotency-Key отсутствует/пуст/длиннее 160 символов.",
            "recovery": "Передайте уникальный ключ на логическую операцию."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Подписка disabled.",
            "recovery": "Включите через PATCH, затем запросите test новым ключом."
          }
        ]
      }
    },
    "/webhooks/{webhookUuid}/deliveries": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "listWebhookDeliveries",
        "summary": "Список доставок",
        "description": "Cursor pagination, новые сначала. Каждая строка отражает последнюю попытку, не историю всех HTTP-вызовов. response_excerpt, тело события, request_id и подпись здесь не раскрываются. Сохраняйте сырые входящие webhook безопасно у себя.\n\nScope: webhooks.read. Bearer-ключ определяет реселлера и среду; подписки не принадлежат одному конкретному API-ключу. Общие правила ошибок и повторов см. руководство надёжности.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "webhooks.read"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookUuid"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeliveryList"
                },
                "examples": {
                  "default": {
                    "value": {
                      "data": [
                        {
                          "uuid": "33333333-3333-4333-8333-333333333333",
                          "event_uuid": "44444444-4444-4444-8444-444444444444",
                          "event_type": "webhook.test",
                          "status": "pending",
                          "attempt_number": 0,
                          "http_status": null,
                          "error_code": null,
                          "next_attempt_at": "2026-10-05T09:01:00+00:00",
                          "delivered_at": null,
                          "failed_at": null
                        }
                      ],
                      "meta": {
                        "next_cursor": null,
                        "per_page": 25
                      }
                    }
                  },
                  "httpFailure": {
                    "value": {
                      "data": [
                        {
                          "uuid": "33333333-3333-4333-8333-333333333333",
                          "event_uuid": "44444444-4444-4444-8444-444444444444",
                          "event_type": "webhook.test",
                          "status": "retry_scheduled",
                          "attempt_number": 1,
                          "http_status": 503,
                          "error_code": "http_error",
                          "next_attempt_at": "2026-10-05T09:02:00+00:00",
                          "delivered_at": null,
                          "failed_at": null
                        }
                      ],
                      "meta": {
                        "next_cursor": null,
                        "per_page": 25
                      }
                    }
                  },
                  "transportFailure": {
                    "value": {
                      "data": [
                        {
                          "uuid": "33333333-3333-4333-8333-333333333333",
                          "event_uuid": "44444444-4444-4444-8444-444444444444",
                          "event_type": "webhook.test",
                          "status": "failed",
                          "attempt_number": 8,
                          "http_status": null,
                          "error_code": "transport_error",
                          "next_attempt_at": null,
                          "delivered_at": null,
                          "failed_at": "2026-10-05T11:08:00+00:00"
                        }
                      ],
                      "meta": {
                        "next_cursor": null,
                        "per_page": 25
                      }
                    }
                  },
                  "cancelled": {
                    "value": {
                      "data": [
                        {
                          "uuid": "33333333-3333-4333-8333-333333333333",
                          "event_uuid": "44444444-4444-4444-8444-444444444444",
                          "event_type": "webhook.test",
                          "status": "cancelled",
                          "attempt_number": 0,
                          "http_status": null,
                          "error_code": "webhook_disabled",
                          "next_attempt_at": null,
                          "delivered_at": null,
                          "failed_at": "2026-10-05T09:02:00+00:00"
                        }
                      ],
                      "meta": {
                        "next_cursor": null,
                        "per_page": 25
                      }
                    }
                  },
                  "delivered": {
                    "value": {
                      "data": [
                        {
                          "uuid": "33333333-3333-4333-8333-333333333333",
                          "event_uuid": "44444444-4444-4444-8444-444444444444",
                          "event_type": "webhook.test",
                          "status": "delivered",
                          "attempt_number": 1,
                          "http_status": 204,
                          "error_code": null,
                          "next_attempt_at": null,
                          "delivered_at": "2026-10-05T09:01:01+00:00",
                          "failed_at": null
                        }
                      ],
                      "meta": {
                        "next_cursor": null,
                        "per_page": 25
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет корректного активного Bearer-ключа либо он истёк/отозван.",
            "recovery": "Получите/исправьте ключ; не повторяйте без изменения причины."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет scope операции, IP запрещён, API не включён для реселлера или его статус/тариф запрещает запрос.",
            "recovery": "Сверьте GET /context и разрешения у провайдера."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "API отключён; для UUID-маршрута также ресурс отсутствует, чужой или другой среды.",
            "recovery": "Проверьте UUID и среду ключа; не перебирайте чужие ресурсы."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпана общая квота этого API-ключа.",
            "recovery": "Ждите Retry-After секунд плюс jitter; запись повторяйте с тем же Idempotency-Key."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Внутренний сбой; некорректный UUID также может не пройти обработку как 422.",
            "recovery": "Сохраните X-Request-ID, передавайте валидный UUID. После записи сначала сверка состояния, не новая операция."
          }
        ]
      }
    },
    "/webhooks/{webhookUuid}/deliveries/{deliveryUuid}/redeliver": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "redeliverWebhook",
        "summary": "Повторить завершённую доставку",
        "description": "Допускаются delivered, failed и cancelled. Повтор использует ту же запись доставки и event id, сбрасывает attempt_number в 0 и ошибки/время завершения, отправляется на текущий URL с текущим секретом. Не создаёт новое бизнес-событие. Получатель должен дедуплицировать повтор.\n\nScope: webhooks.manage. Bearer-ключ определяет реселлера и среду; подписки не принадлежат одному конкретному API-ключу. Общие правила ошибок и повторов см. руководство надёжности.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "x-required-scopes": [
          "webhooks.manage"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookUuid"
          },
          {
            "$ref": "#/components/parameters/DeliveryUuid"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Успешный ответ.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/RequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/IdempotencyReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeliveryResponse"
                },
                "examples": {
                  "default": {
                    "value": {
                      "data": {
                        "uuid": "33333333-3333-4333-8333-333333333333",
                        "event_uuid": "44444444-4444-4444-8444-444444444444",
                        "event_type": "webhook.test",
                        "status": "pending",
                        "attempt_number": 0,
                        "http_status": null,
                        "error_code": null,
                        "next_attempt_at": "2026-10-05T09:01:00+00:00",
                        "delivered_at": null,
                        "failed_at": null
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          }
        },
        "x-error-cases": [
          {
            "status": 401,
            "code": "unauthenticated",
            "when": "Нет корректного активного Bearer-ключа либо он истёк/отозван.",
            "recovery": "Получите/исправьте ключ; не повторяйте без изменения причины."
          },
          {
            "status": 403,
            "code": "forbidden",
            "when": "Нет scope операции, IP запрещён, API не включён для реселлера или его статус/тариф запрещает запрос.",
            "recovery": "Сверьте GET /context и разрешения у провайдера."
          },
          {
            "status": 404,
            "code": "resource_not_found",
            "when": "API отключён; для UUID-маршрута также ресурс отсутствует, чужой или другой среды.",
            "recovery": "Проверьте UUID и среду ключа; не перебирайте чужие ресурсы."
          },
          {
            "status": 429,
            "code": "rate_limit_exceeded",
            "when": "Исчерпана общая квота этого API-ключа.",
            "recovery": "Ждите Retry-After секунд плюс jitter; запись повторяйте с тем же Idempotency-Key."
          },
          {
            "status": 500,
            "code": "internal_error",
            "when": "Внутренний сбой; некорректный UUID также может не пройти обработку как 422.",
            "recovery": "Сохраните X-Request-ID, передавайте валидный UUID. После записи сначала сверка состояния, не новая операция."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Ключ уже обрабатывается или ранее использован с другим method/path/query/body.",
            "recovery": "Не меняйте ключ после неизвестного результата. Дождитесь исходной попытки; исправленный запрос оформляйте новым ключом только после сверки."
          },
          {
            "status": 422,
            "code": "validation_failed",
            "when": "Idempotency-Key отсутствует/пуст/длиннее 160 символов.",
            "recovery": "Передайте уникальный ключ на логическую операцию."
          },
          {
            "status": 409,
            "code": "conflict",
            "when": "Подписка disabled либо доставка pending/retry_scheduled (уже в очереди).",
            "recovery": "Для disabled включите подписку. Для ожидающей доставки дождитесь завершения; не запускайте параллельный redeliver."
          }
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "ErrorEnvelope": {
        "type": "object",
        "description": "Собственный JSON-конверт API, не RFC 9457. Структура и code не зависят от языка; message и details предназначены человеку. До выбора маршрута и на proxy этот конверт не гарантирован.",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "details",
              "request_id"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Машинный код; не извлекайте причину из message.",
                "examples": [
                  "unauthenticated",
                  "forbidden",
                  "resource_not_found",
                  "conflict",
                  "validation_failed",
                  "rate_limit_exceeded",
                  "internal_error",
                  "request_failed"
                ]
              },
              "message": {
                "type": "string",
                "description": "Человекочитаемое, частично локализуемое сообщение; не стабильный идентификатор ошибки."
              },
              "details": {
                "description": "Без подробностей возвращается [], не null и не {}. Валидация: {поле:[сообщения]}. Квота: {retry_after:секунды}.",
                "oneOf": [
                  {
                    "type": "array",
                    "maxItems": 0
                  },
                  {
                    "type": "object",
                    "additionalProperties": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  {
                    "type": "object",
                    "required": [
                      "retry_after"
                    ],
                    "properties": {
                      "retry_after": {
                        "type": "integer",
                        "minimum": 0
                      }
                    },
                    "additionalProperties": false
                  }
                ]
              },
              "request_id": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uuid",
                "description": "UUID для диагностики или null без контекста. При HTTP replay тело может содержать исходный ID, заголовок X-Request-ID относится к текущей попытке."
              }
            }
          }
        }
      },
      "FrameworkError": {
        "type": "object",
        "required": [
          "message"
        ],
        "properties": {
          "message": {
            "type": "string"
          }
        },
        "additionalProperties": true,
        "description": "Неверный метод или неизвестный URL могут отклоняться до reseller-обработчиков; error и X-Request-ID тогда не гарантированы."
      },
      "CursorMeta": {
        "type": "object",
        "required": [
          "next_cursor",
          "per_page"
        ],
        "properties": {
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Непрозрачный курсор следующей страницы; null означает конец."
          },
          "per_page": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "description": "Фактический размер страницы; по умолчанию 25."
          }
        }
      },
      "CoreCursorMeta": {
        "type": "object",
        "description": "Упрощённая курсорная пагинация. total, page и prev_cursor отсутствуют.",
        "required": [
          "next_cursor",
          "per_page"
        ],
        "properties": {
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Передать как cursor следующего запроса. null означает конец."
          },
          "per_page": {
            "type": "integer",
            "format": "int64",
            "description": "Фактический размер страницы.",
            "minimum": 1,
            "maximum": 100
          }
        }
      },
      "CoreResourceMeta": {
        "type": "object",
        "description": "Пагинация Laravel Resource. Не содержит общего количества записей.",
        "required": [
          "path",
          "per_page",
          "next_cursor",
          "prev_cursor"
        ],
        "properties": {
          "path": {
            "type": "string",
            "description": "URL коллекции.",
            "format": "uri"
          },
          "per_page": {
            "type": "integer",
            "format": "int64",
            "description": "Размер страницы.",
            "minimum": 1,
            "maximum": 100
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Непрозрачный курсор следующей страницы."
          },
          "prev_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Непрозрачный курсор предыдущей страницы."
          }
        }
      },
      "CoreResourceLinks": {
        "type": "object",
        "description": "Навигационные ссылки. Курсорная пагинация не вычисляет первую/последнюю страницу. Сохраняйте исходные фильтры при переходе.",
        "required": [
          "first",
          "last",
          "prev",
          "next"
        ],
        "properties": {
          "first": {
            "type": "null",
            "description": "Всегда null."
          },
          "last": {
            "type": "null",
            "description": "Всегда null."
          },
          "prev": {
            "type": [
              "string",
              "null"
            ],
            "description": "Предыдущая страница или null.",
            "format": "uri"
          },
          "next": {
            "type": [
              "string",
              "null"
            ],
            "description": "Следующая страница или null.",
            "format": "uri"
          }
        }
      },
      "CoreContext": {
        "type": "object",
        "description": "Контекст определяется bearer-ключом, а не параметром provider_uuid.",
        "required": [
          "provider",
          "credential"
        ],
        "properties": {
          "provider": {
            "type": "object",
            "description": "Данные реселлера.",
            "required": [
              "uuid",
              "code",
              "name",
              "status",
              "operation_mode",
              "customer_mode",
              "currency",
              "locale",
              "timezone"
            ],
            "properties": {
              "uuid": {
                "type": "string",
                "description": "UUID объекта.",
                "format": "uuid"
              },
              "code": {
                "type": "string",
                "description": "Код реселлера."
              },
              "name": {
                "type": "string",
                "description": "Название."
              },
              "status": {
                "type": "string",
                "description": "Текущий статус реселлера."
              },
              "operation_mode": {
                "type": "string",
                "description": "Режим работы платформы."
              },
              "customer_mode": {
                "type": "string",
                "description": "managed требует customer_uuid; aggregate позволяет опустить его.",
                "enum": [
                  "managed",
                  "aggregate"
                ]
              },
              "currency": {
                "type": "string",
                "description": "Валюта по умолчанию.",
                "minLength": 3,
                "maxLength": 3
              },
              "locale": {
                "type": "string",
                "description": "Язык по умолчанию."
              },
              "timezone": {
                "type": "string",
                "description": "Часовой пояс IANA."
              }
            }
          },
          "credential": {
            "type": "object",
            "description": "Метаданные текущего ключа; секрет не возвращается.",
            "required": [
              "uuid",
              "name",
              "environment",
              "scopes",
              "rate_limit_per_minute",
              "expires_at"
            ],
            "properties": {
              "uuid": {
                "type": "string",
                "description": "UUID объекта.",
                "format": "uuid"
              },
              "name": {
                "type": "string",
                "description": "Имя интеграции."
              },
              "environment": {
                "type": "string",
                "description": "Изолированная среда.",
                "enum": [
                  "test",
                  "live"
                ]
              },
              "scopes": {
                "type": "array",
                "description": "Выданные права.",
                "items": {
                  "type": "string",
                  "description": "Scope."
                }
              },
              "rate_limit_per_minute": {
                "type": "integer",
                "format": "int64",
                "description": "Настроенный лимит ключа. Ограничение применяется общим middleware; учитывайте заголовки ответа."
              },
              "expires_at": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Дата истечения ключа; null означает отсутствие срока.",
                "format": "date-time"
              }
            }
          }
        }
      },
      "CorePeriod": {
        "type": "object",
        "description": "Разрешённый период предложения.",
        "required": [
          "unit",
          "count"
        ],
        "properties": {
          "unit": {
            "type": "string",
            "description": "Единица.",
            "enum": [
              "day",
              "month",
              "year"
            ]
          },
          "count": {
            "type": "integer",
            "format": "int64",
            "description": "Количество.",
            "minimum": 1
          }
        }
      },
      "CoreProduct": {
        "type": "object",
        "description": "Опубликованное предложение продукта. Цены в этом ответе отсутствуют.",
        "required": [
          "uuid",
          "resource_uuid",
          "name",
          "allowed_periods",
          "allowed_operations",
          "retail_pricing_mode",
          "code",
          "type",
          "description"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "description": "UUID предложения. Не передавайте вместо resource_uuid в quotes.",
            "format": "uuid"
          },
          "resource_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "UUID продукта или зоны для quotes.",
            "format": "uuid"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Отображаемое название."
          },
          "allowed_periods": {
            "type": "array",
            "description": "Если пусто, предложение не ограничивает периоды; наличие цены всё равно обязательно.",
            "items": {
              "$ref": "#/components/schemas/CorePeriod"
            }
          },
          "allowed_operations": {
            "type": "array",
            "description": "Если пусто, предложение не ограничивает операции; это не означает наличие API для каждой операции.",
            "items": {
              "type": "string",
              "description": "Операция."
            }
          },
          "retail_pricing_mode": {
            "type": "string",
            "description": "Режим расчёта розничной цены; итог определяется quotes."
          },
          "code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Код продукта."
          },
          "type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Тип продукта, например hosting."
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Описание продукта."
          }
        }
      },
      "CoreZone": {
        "type": "object",
        "description": "Опубликованная доменная зона. Каталог общий для ключей, доступность конкретного test/live регистратора проверяется при операции.",
        "required": [
          "uuid",
          "resource_uuid",
          "name",
          "allowed_periods",
          "allowed_operations",
          "retail_pricing_mode",
          "zone",
          "registration_enabled",
          "renewal_enabled",
          "transfer_enabled",
          "requires_eds_verification",
          "requires_edu_license",
          "min_registration_years",
          "max_registration_years"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "description": "UUID предложения. Не передавайте вместо resource_uuid в quotes.",
            "format": "uuid"
          },
          "resource_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "UUID продукта или зоны для quotes.",
            "format": "uuid"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Отображаемое название."
          },
          "allowed_periods": {
            "type": "array",
            "description": "Если пусто, предложение не ограничивает периоды; наличие цены всё равно обязательно.",
            "items": {
              "$ref": "#/components/schemas/CorePeriod"
            }
          },
          "allowed_operations": {
            "type": "array",
            "description": "Если пусто, предложение не ограничивает операции; это не означает наличие API для каждой операции.",
            "items": {
              "type": "string",
              "description": "Операция."
            }
          },
          "retail_pricing_mode": {
            "type": "string",
            "description": "Режим расчёта розничной цены; итог определяется quotes."
          },
          "zone": {
            "type": [
              "string",
              "null"
            ],
            "description": "Зона без начальной точки, например kz."
          },
          "registration_enabled": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Разрешена регистрация."
          },
          "renewal_enabled": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Разрешено продление."
          },
          "transfer_enabled": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Разрешён трансфер."
          },
          "requires_eds_verification": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Требуется проверка ЭЦП."
          },
          "requires_edu_license": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Требуется образовательная лицензия."
          },
          "min_registration_years": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Минимальный срок регистрации."
          },
          "max_registration_years": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Максимальный срок регистрации."
          }
        }
      },
      "CoreIndividualProfile": {
        "type": "object",
        "description": "Профиль физического лица.",
        "required": [
          "first_name",
          "last_name",
          "middle_name",
          "birth_date",
          "country_code",
          "city",
          "address_line",
          "postal_code"
        ],
        "properties": {
          "first_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Имя."
          },
          "last_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Фамилия."
          },
          "middle_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Отчество."
          },
          "birth_date": {
            "type": [
              "string",
              "null"
            ],
            "description": "Дата рождения.",
            "format": "date"
          },
          "country_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Страна, код из двух символов."
          },
          "city": {
            "type": [
              "string",
              "null"
            ],
            "description": "Город."
          },
          "address_line": {
            "type": [
              "string",
              "null"
            ],
            "description": "Адрес."
          },
          "postal_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Почтовый индекс."
          }
        }
      },
      "CoreLegalProfile": {
        "type": "object",
        "description": "Профиль юридического лица.",
        "required": [
          "company_name",
          "registration_number",
          "tax_number",
          "legal_address",
          "actual_address",
          "director_full_name"
        ],
        "properties": {
          "company_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Название организации."
          },
          "registration_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "Регистрационный номер."
          },
          "tax_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "Налоговый номер."
          },
          "legal_address": {
            "type": [
              "string",
              "null"
            ],
            "description": "Юридический адрес."
          },
          "actual_address": {
            "type": [
              "string",
              "null"
            ],
            "description": "Фактический адрес."
          },
          "director_full_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "ФИО руководителя."
          }
        }
      },
      "CoreCustomer": {
        "type": "object",
        "description": "Покупатель биллинга, не контакт реестра и не bearer-ключ.",
        "required": [
          "uuid",
          "external_id",
          "type",
          "status",
          "display_name",
          "email",
          "phone",
          "preferred_currency",
          "locale",
          "timezone",
          "profile",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "description": "UUID объекта.",
            "format": "uuid"
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Идентификатор клиента во внешнем биллинге."
          },
          "type": {
            "type": "string",
            "description": "Тип.",
            "enum": [
              "individual",
              "legal"
            ]
          },
          "status": {
            "type": "string",
            "description": "Состояние клиента."
          },
          "display_name": {
            "type": "string",
            "description": "Отображаемое имя."
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "description": "Email клиента.",
            "format": "email"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "Телефон."
          },
          "preferred_currency": {
            "type": "string",
            "description": "Предпочитаемая валюта."
          },
          "locale": {
            "type": "string",
            "description": "Язык."
          },
          "timezone": {
            "type": "string",
            "description": "Часовой пояс."
          },
          "profile": {
            "description": "Для legal возвращается юридический профиль, для individual физический.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/CoreIndividualProfile"
              },
              {
                "$ref": "#/components/schemas/CoreLegalProfile"
              }
            ]
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Создан.",
            "format": "date-time"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Изменён.",
            "format": "date-time"
          }
        }
      },
      "CoreCreateCustomerRequest": {
        "type": "object",
        "description": "Создание доступно только customer_mode=managed. Поля профиля передаются на верхнем уровне, не в profile. API не возвращает пароль. test создаёт изолированного неактивного пользователя, не использует live-учётную запись по email.",
        "required": [
          "type",
          "display_name",
          "email"
        ],
        "properties": {
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Уникален внутри реселлера и среды test/live.",
            "maxLength": 255
          },
          "type": {
            "type": "string",
            "description": "Тип клиента.",
            "enum": [
              "individual",
              "legal"
            ]
          },
          "status": {
            "type": "string",
            "description": "При создании принимается, но итоговый статус всегда active. При PATCH изменяется.",
            "enum": [
              "active",
              "blocked"
            ]
          },
          "display_name": {
            "type": "string",
            "description": "Отображаемое имя.",
            "maxLength": 255
          },
          "email": {
            "type": "string",
            "description": "Email; при создании приводится к нижнему регистру.",
            "format": "email",
            "maxLength": 255
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "Телефон; null при PATCH не очищает существующее значение.",
            "maxLength": 50
          },
          "preferred_currency": {
            "type": "string",
            "description": "Валюта, при создании верхний регистр.",
            "minLength": 3,
            "maxLength": 3
          },
          "locale": {
            "type": "string",
            "description": "Язык.",
            "enum": [
              "ru",
              "kk",
              "en"
            ]
          },
          "timezone": {
            "type": "string",
            "description": "Допустимый часовой пояс IANA."
          },
          "company_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для legal; при отсутствии используется display_name. При PATCH отсутствие может заменить название текущим display_name.",
            "maxLength": 255
          },
          "registration_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для legal.",
            "maxLength": 100
          },
          "tax_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для legal.",
            "maxLength": 100
          },
          "legal_address": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для legal.",
            "maxLength": 500
          },
          "actual_address": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для legal.",
            "maxLength": 500
          },
          "director_full_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для legal.",
            "maxLength": 255
          },
          "first_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для individual; при создании по умолчанию display_name.",
            "maxLength": 100
          },
          "last_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для individual; при создании по умолчанию display_name.",
            "maxLength": 100
          },
          "middle_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для individual.",
            "maxLength": 100
          },
          "birth_date": {
            "type": [
              "string",
              "null"
            ],
            "description": "Дата строго раньше сегодняшней.",
            "format": "date"
          },
          "country_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Код страны; по умолчанию KZ.",
            "minLength": 2,
            "maxLength": 2
          },
          "city": {
            "type": [
              "string",
              "null"
            ],
            "description": "Город.",
            "maxLength": 120
          },
          "address_line": {
            "type": [
              "string",
              "null"
            ],
            "description": "Адрес.",
            "maxLength": 500
          },
          "postal_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Индекс.",
            "maxLength": 30
          }
        }
      },
      "CoreUpdateCustomerRequest": {
        "type": "object",
        "description": "Частичное обновление. type, email, preferred_currency проходят валидацию, но текущий контроллер НЕ изменяет их. Не используйте PATCH для смены этих полей. null для полей профиля сохраняет прежнее значение. Не передавайте вложенный profile.",
        "required": [],
        "properties": {
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Уникален внутри реселлера и среды test/live.",
            "maxLength": 255
          },
          "type": {
            "type": "string",
            "description": "При PATCH принимается, но не изменяется.",
            "enum": [
              "individual",
              "legal"
            ]
          },
          "status": {
            "type": "string",
            "description": "При создании принимается, но итоговый статус всегда active. При PATCH изменяется.",
            "enum": [
              "active",
              "blocked"
            ]
          },
          "display_name": {
            "type": "string",
            "description": "Отображаемое имя.",
            "maxLength": 255
          },
          "email": {
            "type": "string",
            "description": "При PATCH принимается, но не изменяется.",
            "format": "email",
            "maxLength": 255
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "Телефон; null при PATCH не очищает существующее значение.",
            "maxLength": 50
          },
          "preferred_currency": {
            "type": "string",
            "description": "При PATCH принимается, но не изменяется.",
            "minLength": 3,
            "maxLength": 3
          },
          "locale": {
            "type": "string",
            "description": "Язык.",
            "enum": [
              "ru",
              "kk",
              "en"
            ]
          },
          "timezone": {
            "type": "string",
            "description": "Допустимый часовой пояс IANA."
          },
          "company_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для legal; при отсутствии используется display_name. При PATCH отсутствие может заменить название текущим display_name.",
            "maxLength": 255
          },
          "registration_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для legal.",
            "maxLength": 100
          },
          "tax_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для legal.",
            "maxLength": 100
          },
          "legal_address": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для legal.",
            "maxLength": 500
          },
          "actual_address": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для legal.",
            "maxLength": 500
          },
          "director_full_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для legal.",
            "maxLength": 255
          },
          "first_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для individual; при создании по умолчанию display_name.",
            "maxLength": 100
          },
          "last_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для individual; при создании по умолчанию display_name.",
            "maxLength": 100
          },
          "middle_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для individual.",
            "maxLength": 100
          },
          "birth_date": {
            "type": [
              "string",
              "null"
            ],
            "description": "Дата строго раньше сегодняшней.",
            "format": "date"
          },
          "country_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Код страны; по умолчанию KZ.",
            "minLength": 2,
            "maxLength": 2
          },
          "city": {
            "type": [
              "string",
              "null"
            ],
            "description": "Город.",
            "maxLength": 120
          },
          "address_line": {
            "type": [
              "string",
              "null"
            ],
            "description": "Адрес.",
            "maxLength": 500
          },
          "postal_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Индекс.",
            "maxLength": 30
          }
        }
      },
      "CoreContact": {
        "type": "object",
        "description": "Локальный доменный контакт. external_id_value никогда не возвращается. Изменение этой карточки само по себе не выполняет EPP contact update.",
        "required": [
          "uuid",
          "type",
          "status",
          "first_name",
          "last_name",
          "middle_name",
          "organization_name",
          "email",
          "phone",
          "country_code",
          "residence_country_code",
          "city",
          "region",
          "address_line",
          "postal_code",
          "external_id_type",
          "verification_status",
          "is_locked_as_registrant",
          "verified_at",
          "external_id",
          "customer_uuid"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "description": "UUID объекта.",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "description": "Тип.",
            "enum": [
              "person",
              "organization"
            ]
          },
          "status": {
            "type": "string",
            "description": "Статус, при создании active."
          },
          "first_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Имя."
          },
          "last_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Фамилия."
          },
          "middle_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Отчество."
          },
          "organization_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Организация."
          },
          "email": {
            "type": "string",
            "description": "Email.",
            "format": "email"
          },
          "phone": {
            "type": "string",
            "description": "Телефон."
          },
          "country_code": {
            "type": "string",
            "description": "Страна адреса."
          },
          "residence_country_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Страна резидентства."
          },
          "city": {
            "type": "string",
            "description": "Город."
          },
          "region": {
            "type": [
              "string",
              "null"
            ],
            "description": "Регион."
          },
          "address_line": {
            "type": "string",
            "description": "Адрес."
          },
          "postal_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Почтовый индекс."
          },
          "external_id_type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Тип документа, не идентификатор внешнего биллинга."
          },
          "verification_status": {
            "type": "string",
            "description": "Статус верификации; создание обычно not_required."
          },
          "is_locked_as_registrant": {
            "type": "boolean",
            "description": "Признак блокировки контакта как владельца."
          },
          "verified_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Время подтверждения.",
            "format": "date-time"
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Идентификатор контакта во внешнем биллинге."
          },
          "customer_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "UUID покупателя.",
            "format": "uuid"
          }
        }
      },
      "CoreUpsertContactRequest": {
        "type": "object",
        "description": "POST и PATCH используют одинаковую полную валидацию: PATCH не частичный. Неуказанные необязательные текстовые поля обнуляются; external_id сохраняется при отсутствии. Документ при отсутствии обоих полей сохраняется. customer_uuid обязателен в managed.",
        "required": [
          "type",
          "email",
          "phone",
          "country_code",
          "residence_country_code",
          "city",
          "address_line",
          "postal_code"
        ],
        "properties": {
          "customer_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "Обязателен в managed. При aggregate можно опустить для агрегированного покупателя.",
            "format": "uuid"
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Идентификатор во внешнем биллинге; уникален для реселлера+среды.",
            "maxLength": 255
          },
          "type": {
            "type": "string",
            "description": "Тип контакта.",
            "enum": [
              "person",
              "organization"
            ]
          },
          "first_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Обязателен для person. Unicode-буквы/диакритика, одиночные пробелы, дефис или апостроф между словами.",
            "maxLength": 255
          },
          "last_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Обязателен для person; те же правила имени.",
            "maxLength": 255
          },
          "middle_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "В текущем API отчество ОБЯЗАТЕЛЬНО для person, не только имя и фамилия.",
            "maxLength": 255
          },
          "organization_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Обязательно для organization.",
            "maxLength": 500
          },
          "email": {
            "type": "string",
            "description": "Email.",
            "format": "email",
            "maxLength": 255
          },
          "phone": {
            "type": "string",
            "description": "Телефон. Контроллер ограничивает длину, не проверяет E.164; используйте международный формат.",
            "maxLength": 50
          },
          "country_code": {
            "type": "string",
            "description": "Страна адреса, верхний регистр сохраняется сервером.",
            "minLength": 2,
            "maxLength": 2
          },
          "residence_country_code": {
            "type": "string",
            "description": "Обязательная страна резидентства; определяет правила документа.",
            "minLength": 2,
            "maxLength": 2
          },
          "city": {
            "type": "string",
            "description": "Город.",
            "maxLength": 255
          },
          "region": {
            "type": [
              "string",
              "null"
            ],
            "description": "Регион.",
            "maxLength": 255
          },
          "address_line": {
            "type": "string",
            "description": "Адрес.",
            "maxLength": 1000
          },
          "postal_code": {
            "type": "string",
            "description": "Обязательный индекс.",
            "maxLength": 30
          },
          "external_id_type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Обязателен с external_id_value. KZ: BIN/IIN (12 цифр и контрольная сумма); RU: INN (10/12 цифр); US: EIN (NN-NNNNNNN), SSN (NNN-NN-NNNN); CN: USCC (18 символов); DE: VAT (DE+9 цифр); UZ: STIR (9), PINFL (14); GB: CRN (8), VAT (GB+9); TR: VKN (10), TCKN (11); UA: EDRPOU (8); AE: TRN (100+12). Для остальных стран PASSPORT/TAX_ID (непустое значение). USCC допускает [0-9A-HJ-NP-RT-UW-Y]{18}. Форматы регистрозависимы для значения документа, тип/страна нормализуются к верхнему регистру.",
            "maxLength": 40
          },
          "external_id_value": {
            "type": [
              "string",
              "null"
            ],
            "description": "Обязателен с external_id_type; хранится, но не возвращается. Для KZ проверяется контрольная сумма; тестовые исключения зависят от конфигурации, test-ключ не отключает проверку.",
            "maxLength": 100,
            "writeOnly": true
          }
        },
        "allOf": [
          {
            "if": {
              "properties": {
                "type": {
                  "const": "person"
                }
              }
            },
            "then": {
              "required": [
                "first_name",
                "last_name",
                "middle_name"
              ],
              "properties": {
                "first_name": {
                  "type": "string",
                  "minLength": 1
                },
                "last_name": {
                  "type": "string",
                  "minLength": 1
                },
                "middle_name": {
                  "type": "string",
                  "minLength": 1
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "organization"
                }
              }
            },
            "then": {
              "required": [
                "organization_name"
              ],
              "properties": {
                "organization_name": {
                  "type": "string",
                  "minLength": 1
                }
              }
            }
          }
        ]
      },
      "CoreContactDeleted": {
        "type": "object",
        "description": "Удаление локального контакта.",
        "required": [
          "uuid",
          "deleted"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "description": "UUID объекта.",
            "format": "uuid"
          },
          "deleted": {
            "type": "boolean",
            "const": true,
            "description": "Контакт удалён локально. Это не удаление домена в реестре."
          }
        }
      },
      "CoreNameserver": {
        "type": "object",
        "description": "NS домена; IP передаются при необходимости glue.",
        "required": [
          "hostname"
        ],
        "properties": {
          "hostname": {
            "type": "string",
            "description": "Имя NS.",
            "maxLength": 253
          },
          "ipv4": {
            "type": [
              "string",
              "null"
            ],
            "description": "IPv4.",
            "format": "ipv4"
          },
          "ipv6": {
            "type": [
              "string",
              "null"
            ],
            "description": "IPv6.",
            "format": "ipv6"
          }
        }
      },
      "CoreContactRoles": {
        "type": "object",
        "description": "Все четыре роли обязательны при регистрации; допустим один UUID для нескольких ролей.",
        "required": [
          "owner",
          "admin",
          "tech",
          "billing"
        ],
        "properties": {
          "owner": {
            "type": "string",
            "description": "Владелец.",
            "format": "uuid"
          },
          "admin": {
            "type": "string",
            "description": "Административный контакт.",
            "format": "uuid"
          },
          "tech": {
            "type": "string",
            "description": "Технический контакт.",
            "format": "uuid"
          },
          "billing": {
            "type": "string",
            "description": "Платёжный контакт.",
            "format": "uuid"
          }
        }
      },
      "CoreQuotePayload": {
        "type": "object",
        "description": "Payload сохраняется в котировке. Для register обязательность domain_name/contact_uuids и ограничения NS проверяются при создании заказа, а не только quotes. Для renew/restore укажите domain_service_uuid (domain_uuid поддерживается как альтернативное имя). Для product/order передавайте domain_name для хостинга. Неизвестные поля могут храниться, но не получают автоматической семантики. Для product/order этот контракт гарантирует только поля, реально используемые OrderCreator: domain_name, notes, external_id. Произвольный payload не передаётся автоматически как команда Plesk.",
        "required": [],
        "properties": {
          "domain_name": {
            "type": "string",
            "description": "Домен, соответствующий зоне; для hosting домен сайта. Для хостинга отсутствие/ошибка домена может проявиться при асинхронном provisioning, даже если quote/заказ приняты.",
            "maxLength": 253
          },
          "contact_uuids": {
            "$ref": "#/components/schemas/CoreContactRoles"
          },
          "nameservers": {
            "type": "array",
            "description": "Необязательно при регистрации; при наличии от 2 до 6, hostname уникальны без учёта регистра.",
            "items": {
              "$ref": "#/components/schemas/CoreNameserver"
            },
            "minItems": 2,
            "maxItems": 6
          },
          "purpose": {
            "type": [
              "string",
              "null"
            ],
            "description": "Назначение домена.",
            "maxLength": 1000
          },
          "notes": {
            "type": [
              "string",
              "null"
            ],
            "description": "Примечание заказа.",
            "maxLength": 1000
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Идентификатор создаваемой услуги, уникален для реселлера+среды.",
            "maxLength": 255
          },
          "whois_privacy_enabled": {
            "type": "boolean",
            "description": "Запрос приватности при регистрации; поддержка зависит от регистратора."
          },
          "domain_service_uuid": {
            "type": "string",
            "description": "UUID домена для операции над существующим доменом.",
            "format": "uuid"
          },
          "domain_uuid": {
            "type": "string",
            "description": "Альтернативное имя domain_service_uuid.",
            "format": "uuid"
          }
        },
        "additionalProperties": true
      },
      "CoreQuoteInputItem": {
        "type": "object",
        "description": "Цена выводится из опубликованного предложения, тарифного плана и правил реселлера.",
        "required": [
          "resource_type",
          "resource_uuid",
          "operation",
          "period_unit",
          "period_count"
        ],
        "properties": {
          "resource_type": {
            "type": "string",
            "description": "Тип ресурса.",
            "enum": [
              "product",
              "domain_zone"
            ]
          },
          "resource_uuid": {
            "type": "string",
            "description": "resource_uuid из каталога; не uuid предложения.",
            "format": "uuid"
          },
          "operation": {
            "type": "string",
            "description": "Допустимые значения валидатора. order/register исполняются через orders; renew/transfer/restore через доменные методы. Наличие add-on в валидаторе НЕ означает отдельный метод исполнения add-on.",
            "enum": [
              "order",
              "add-on",
              "register",
              "renew",
              "transfer",
              "restore"
            ]
          },
          "period_unit": {
            "type": "string",
            "description": "Для доменов только year.",
            "enum": [
              "day",
              "month",
              "year"
            ]
          },
          "period_count": {
            "type": "integer",
            "format": "int64",
            "description": "От 1 до 120 на уровне quote. Регистрация домена дополнительно ограничена 1..10 годами; transfer/restore используют 1 год.",
            "minimum": 1,
            "maximum": 120
          },
          "quantity": {
            "type": "integer",
            "format": "int64",
            "description": "По умолчанию 1. При регистрации/платных доменных операциях ровно 1.",
            "minimum": 1,
            "maximum": 100,
            "default": 1
          },
          "currency": {
            "type": "string",
            "description": "Код длиной 3. Валюта всей котировки выбирается из первой строки либо настроек реселлера; currency последующих строк не переключает валюту. Передавайте одинаковую валюту во всех строках.",
            "minLength": 3,
            "maxLength": 3
          },
          "payload": {
            "$ref": "#/components/schemas/CoreQuotePayload"
          }
        },
        "additionalProperties": false
      },
      "CoreCreateQuoteRequest": {
        "type": "object",
        "description": "Котировка привязывается к текущему ключу, реселлеру, покупателю и среде. Срок задаётся сервером, по умолчанию 15 минут; используйте expires_at. Создание котировки не регистрирует домен и не резервирует имя.",
        "required": [
          "items"
        ],
        "properties": {
          "customer_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "Обязателен в managed; aggregate допускает отсутствие.",
            "format": "uuid"
          },
          "items": {
            "type": "array",
            "description": "Строки котировки.",
            "items": {
              "$ref": "#/components/schemas/CoreQuoteInputItem"
            },
            "minItems": 1,
            "maxItems": 100
          }
        }
      },
      "CoreQuoteItem": {
        "type": "object",
        "description": "Розничные суммы строки. Закупочная цена, себестоимость и resource_uuid не возвращаются.",
        "required": [
          "uuid",
          "resource_type",
          "operation",
          "period_unit",
          "period_count",
          "quantity",
          "currency",
          "unit_price_minor",
          "subtotal_minor",
          "discount_minor",
          "tax_minor",
          "total_minor"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "description": "UUID строки котировки.",
            "format": "uuid"
          },
          "resource_type": {
            "type": "string",
            "description": "Тип ресурса."
          },
          "operation": {
            "type": "string",
            "description": "Операция."
          },
          "period_unit": {
            "type": "string",
            "description": "Единица периода."
          },
          "period_count": {
            "type": "integer",
            "format": "int64",
            "description": "Количество периодов."
          },
          "quantity": {
            "type": "integer",
            "format": "int64",
            "description": "Количество."
          },
          "currency": {
            "type": "string",
            "description": "Валюта."
          },
          "unit_price_minor": {
            "type": "integer",
            "format": "int64",
            "description": "Цена единицы. В минимальных единицах валюты, без десятичных дробей."
          },
          "subtotal_minor": {
            "type": "integer",
            "format": "int64",
            "description": "Подытог. В минимальных единицах валюты, без десятичных дробей."
          },
          "discount_minor": {
            "type": "integer",
            "format": "int64",
            "description": "Скидка. В минимальных единицах валюты, без десятичных дробей."
          },
          "tax_minor": {
            "type": "integer",
            "format": "int64",
            "description": "Налог. В минимальных единицах валюты, без десятичных дробей."
          },
          "total_minor": {
            "type": "integer",
            "format": "int64",
            "description": "Итого строки. В минимальных единицах валюты, без десятичных дробей."
          }
        }
      },
      "CoreQuote": {
        "type": "object",
        "description": "Выданная котировка. Нет GET /quotes/{uuid}; сохраняйте этот ответ.",
        "required": [
          "uuid",
          "customer_uuid",
          "status",
          "environment",
          "currency",
          "issued_at",
          "expires_at",
          "items",
          "total_minor"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "description": "UUID объекта.",
            "format": "uuid"
          },
          "customer_uuid": {
            "type": "string",
            "description": "Покупатель.",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "description": "При создании active. Позже котировка может стать consumed/expired; отдельного GET нет."
          },
          "environment": {
            "type": "string",
            "description": "Среда.",
            "enum": [
              "test",
              "live"
            ]
          },
          "currency": {
            "type": "string",
            "description": "Валюта."
          },
          "issued_at": {
            "type": "string",
            "description": "Выдана.",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "description": "Истекает.",
            "format": "date-time"
          },
          "items": {
            "type": "array",
            "description": "Строки.",
            "items": {
              "$ref": "#/components/schemas/CoreQuoteItem"
            }
          },
          "total_minor": {
            "type": "integer",
            "format": "int64",
            "description": "Сумма total_minor всех строк. В минимальных единицах валюты, без десятичных дробей."
          }
        }
      },
      "CoreCreateOrderRequest": {
        "type": "object",
        "description": "Создаёт заказ по ранее полученной котировке, а не по произвольной цене. Тот же ключ, покупатель и среда обязательны. Поддерживаются только operation=order/register. Тело одинаково для /orders, /domains и /hosting/orders; последние два маршрута добавляют scopes, но не фильтруют типы строк.",
        "required": [
          "quote_uuid"
        ],
        "properties": {
          "quote_uuid": {
            "type": "string",
            "description": "UUID неистёкшей, неиспользованной котировки текущего ключа.",
            "format": "uuid"
          },
          "customer_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "Покупатель котировки; обязателен в managed.",
            "format": "uuid"
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "ID заказа внешнего биллинга; уникален у реселлера внутри test/live.",
            "maxLength": 255
          }
        }
      },
      "CoreOrderItem": {
        "type": "object",
        "description": "Строка заказа.",
        "required": [
          "uuid",
          "type",
          "name",
          "quantity",
          "unit_price_minor",
          "total_minor",
          "currency",
          "service_uuid",
          "service_external_id"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "description": "UUID объекта.",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "description": "Тип услуги, например domain/hosting."
          },
          "name": {
            "type": "string",
            "description": "Название."
          },
          "quantity": {
            "type": "integer",
            "format": "int64",
            "description": "Количество."
          },
          "unit_price_minor": {
            "type": "integer",
            "format": "int64",
            "description": "Цена единицы. В минимальных единицах валюты, без десятичных дробей."
          },
          "total_minor": {
            "type": "integer",
            "format": "int64",
            "description": "Итого. В минимальных единицах валюты, без десятичных дробей."
          },
          "currency": {
            "type": "string",
            "description": "Валюта."
          },
          "service_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "UUID созданной услуги. Отличается от domain UUID.",
            "format": "uuid"
          },
          "service_external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "external_id из payload строки котировки."
          }
        }
      },
      "CoreInvoice": {
        "type": "object",
        "description": "Связанный счёт.",
        "required": [
          "uuid",
          "number",
          "status",
          "currency",
          "total_minor",
          "paid_minor",
          "payment_collected_by_platform"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "description": "UUID объекта.",
            "format": "uuid"
          },
          "number": {
            "type": "string",
            "description": "Номер."
          },
          "status": {
            "type": "string",
            "description": "Состояние счёта, например issued/paid/cancelled."
          },
          "currency": {
            "type": "string",
            "description": "Валюта."
          },
          "total_minor": {
            "type": "integer",
            "format": "int64",
            "description": "Сумма. В минимальных единицах валюты, без десятичных дробей."
          },
          "paid_minor": {
            "type": "integer",
            "format": "int64",
            "description": "Оплачено. В минимальных единицах валюты, без десятичных дробей."
          },
          "payment_collected_by_platform": {
            "type": "boolean",
            "description": "false при external checkout: оплату клиента принимает реселлер. paid не означает списание с карты платформой."
          }
        }
      },
      "CoreOrder": {
        "type": "object",
        "description": "Заказ принят не равнозначно домен зарегистрирован. В external checkout выставляется оплаченный счёт и планируется асинхронное исполнение; дальнейшее состояние отслеживайте через заказ/услугу и webhooks.",
        "required": [
          "uuid",
          "external_id",
          "customer_uuid",
          "number",
          "status",
          "currency",
          "total_minor",
          "submitted_at",
          "paid_at",
          "completed_at",
          "failed_at",
          "failure_reason",
          "items",
          "invoice",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "description": "UUID объекта.",
            "format": "uuid"
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "ID заказа внешнего биллинга."
          },
          "customer_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "UUID покупателя.",
            "format": "uuid"
          },
          "number": {
            "type": "string",
            "description": "Номер заказа."
          },
          "status": {
            "type": "string",
            "description": "Состояние заказа. submitted: выставлен счёт; paid: оплата зафиксирована; registration_processing/registry_pending: работа с реестром; active/completed: исполнен; registration_failed: регистрация неуспешна. Даты и invoice отражают отдельные стадии, HTTP 201 не терминальный статус.",
            "enum": [
              "draft",
              "submitted",
              "payment_authorized",
              "registration_processing",
              "registry_pending",
              "paid",
              "active",
              "completed",
              "registration_failed",
              "cancelled",
              "refunded"
            ]
          },
          "currency": {
            "type": "string",
            "description": "Валюта."
          },
          "total_minor": {
            "type": "integer",
            "format": "int64",
            "description": "Сумма. В минимальных единицах валюты, без десятичных дробей."
          },
          "submitted_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Подан.",
            "format": "date-time"
          },
          "paid_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Оплачен.",
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Завершён.",
            "format": "date-time"
          },
          "failed_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Ошибка.",
            "format": "date-time"
          },
          "failure_reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Причина ошибки; не используйте текст как стабильный машинный код."
          },
          "items": {
            "type": "array",
            "description": "Строки.",
            "items": {
              "$ref": "#/components/schemas/CoreOrderItem"
            }
          },
          "invoice": {
            "description": "Счёт или null.",
            "anyOf": [
              {
                "$ref": "#/components/schemas/CoreInvoice"
              },
              {
                "type": "null"
              }
            ]
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Создан.",
            "format": "date-time"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Изменён.",
            "format": "date-time"
          }
        }
      },
      "CoreServiceDomain": {
        "type": "object",
        "description": "Краткое состояние домена. Полная карточка доступна через /domains/{domainUuid}.",
        "required": [
          "uuid",
          "external_id",
          "domain_name",
          "status",
          "registration_status",
          "verification_status",
          "registry_status"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "description": "UUID домена.",
            "format": "uuid"
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "ID внешнего биллинга."
          },
          "domain_name": {
            "type": "string",
            "description": "Домен."
          },
          "status": {
            "type": "string",
            "description": "Статус услуги домена."
          },
          "registration_status": {
            "type": "string",
            "description": "Состояние регистрации."
          },
          "verification_status": {
            "type": "string",
            "description": "Состояние проверки."
          },
          "registry_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Статус реестра."
          }
        }
      },
      "CoreServiceHosting": {
        "type": "object",
        "description": "Краткое состояние хостинга. Паролей, panel URL и SSO в этом ресурсе нет.",
        "required": [
          "uuid",
          "domain_name",
          "status",
          "plan_code",
          "provisioned_at"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "description": "UUID объекта.",
            "format": "uuid"
          },
          "domain_name": {
            "type": "string",
            "description": "Домен хостинга."
          },
          "status": {
            "type": "string",
            "description": "Статус хостинга."
          },
          "plan_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Код тарифа."
          },
          "provisioned_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Дата фактического создания.",
            "format": "date-time"
          }
        }
      },
      "CoreService": {
        "type": "object",
        "description": "Услуга. Список изолирован по реселлеру и fulfillment_environment; доменная связь дополнительно проверяется по среде регистратора.",
        "required": [
          "uuid",
          "external_id",
          "customer_uuid",
          "type",
          "status",
          "name",
          "starts_at",
          "expires_at",
          "auto_renew",
          "domain",
          "hosting",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "description": "UUID объекта.",
            "format": "uuid"
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "ID услуги внешнего биллинга."
          },
          "customer_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "UUID покупателя.",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "description": "Тип услуги."
          },
          "status": {
            "type": "string",
            "description": "Состояние, например pending/provisioning/active/suspended/failed/cancelled."
          },
          "name": {
            "type": "string",
            "description": "Название."
          },
          "starts_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Начало периода.",
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Конец периода.",
            "format": "date-time"
          },
          "auto_renew": {
            "type": "boolean",
            "description": "Общий флаг услуги. Это не reseller API согласие на автопродление домена; для него используйте /domains/{uuid}/auto-renew."
          },
          "domain": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/CoreServiceDomain"
              },
              {
                "type": "null"
              }
            ],
            "description": "Краткая карточка домена или null, в том числе до создания доменной записи."
          },
          "hosting": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/CoreServiceHosting"
              },
              {
                "type": "null"
              }
            ],
            "description": "Карточка хостинга или null."
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Создана.",
            "format": "date-time"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Изменена.",
            "format": "date-time"
          }
        }
      },
      "CoreBalance": {
        "type": "object",
        "description": "Закупочный счёт реселлера, не баланс конечного клиента. Только live.",
        "required": [
          "currency",
          "available_minor",
          "held_minor"
        ],
        "properties": {
          "currency": {
            "type": "string",
            "description": "Валюта."
          },
          "available_minor": {
            "type": "integer",
            "format": "int64",
            "description": "Доступно. В минимальных единицах валюты, без десятичных дробей."
          },
          "held_minor": {
            "type": "integer",
            "format": "int64",
            "description": "Зарезервировано; не равно окончательно списанным средствам. В минимальных единицах валюты, без десятичных дробей."
          }
        }
      },
      "CoreLedgerEntry": {
        "type": "object",
        "description": "Проводка закупочного счёта. Возвращаются только публичные поля; ссылок на внутренние транзакции нет.",
        "required": [
          "uuid",
          "kind",
          "amount_minor",
          "currency",
          "available_balance_minor",
          "held_balance_minor",
          "description",
          "occurred_at"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "description": "UUID объекта.",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "description": "Вид проводки, например provider_reserve. Набор открыт; не вычисляйте баланс только по kind и amount_minor."
          },
          "amount_minor": {
            "type": "integer",
            "format": "int64",
            "description": "Сумма операции. В минимальных единицах валюты, без десятичных дробей."
          },
          "currency": {
            "type": "string",
            "description": "Валюта."
          },
          "available_balance_minor": {
            "type": "integer",
            "format": "int64",
            "description": "Доступный остаток после операции. В минимальных единицах валюты, без десятичных дробей."
          },
          "held_balance_minor": {
            "type": "integer",
            "format": "int64",
            "description": "Зарезервированный остаток после операции. В минимальных единицах валюты, без десятичных дробей."
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Описание."
          },
          "occurred_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Время проводки.",
            "format": "date-time"
          }
        }
      },
      "CoreTicketAttachment": {
        "type": "object",
        "description": "Метаданные вложения. Этот API не предоставляет upload/download endpoint или URL содержимого.",
        "required": [
          "uuid",
          "name",
          "mime_type",
          "size_bytes"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "description": "UUID объекта.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "Исходное имя."
          },
          "mime_type": {
            "type": "string",
            "description": "MIME."
          },
          "size_bytes": {
            "type": "integer",
            "format": "int64",
            "description": "Байты.",
            "minimum": 0
          }
        }
      },
      "CoreTicketMessage": {
        "type": "object",
        "description": "Только публичное сообщение; внутренние заметки не возвращаются.",
        "required": [
          "uuid",
          "type",
          "body",
          "sender_type",
          "created_at",
          "attachments"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "description": "UUID объекта.",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "description": "Тип сообщения."
          },
          "body": {
            "type": "string",
            "description": "Текст."
          },
          "sender_type": {
            "type": "string",
            "description": "Сторона.",
            "enum": [
              "customer",
              "support"
            ]
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Создано.",
            "format": "date-time"
          },
          "attachments": {
            "type": "array",
            "description": "Метаданные вложений.",
            "items": {
              "$ref": "#/components/schemas/CoreTicketAttachment"
            }
          }
        }
      },
      "CoreTicketSummary": {
        "type": "object",
        "description": "Тикет в списке. Поле messages отсутствует, а не пустой массив.",
        "required": [
          "uuid",
          "external_id",
          "customer_uuid",
          "number",
          "subject",
          "status",
          "priority",
          "department",
          "category",
          "service_uuid",
          "domain_uuid",
          "order_uuid",
          "last_message_at",
          "closed_at",
          "messages_count",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "description": "UUID объекта.",
            "format": "uuid"
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "ID тикета внешнего биллинга."
          },
          "customer_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "Покупатель.",
            "format": "uuid"
          },
          "number": {
            "type": "string",
            "description": "Номер."
          },
          "subject": {
            "type": "string",
            "description": "Тема."
          },
          "status": {
            "type": "string",
            "description": "Состояние; ответ клиента открывает тикет, close_ticket=true закрывает."
          },
          "priority": {
            "type": "string",
            "description": "Приоритет.",
            "enum": [
              "low",
              "normal",
              "high",
              "urgent"
            ]
          },
          "department": {
            "type": [
              "string",
              "null"
            ],
            "description": "Отдел."
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "description": "Категория."
          },
          "service_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "Связанная услуга.",
            "format": "uuid"
          },
          "domain_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "Связанный домен.",
            "format": "uuid"
          },
          "order_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "Связанный заказ.",
            "format": "uuid"
          },
          "last_message_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Последнее сообщение.",
            "format": "date-time"
          },
          "closed_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Закрыт.",
            "format": "date-time"
          },
          "messages_count": {
            "type": "integer",
            "format": "int64",
            "description": "Количество публичных сообщений.",
            "minimum": 0
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Создан.",
            "format": "date-time"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Изменён.",
            "format": "date-time"
          }
        }
      },
      "CoreTicket": {
        "type": "object",
        "description": "Тикет с полной публичной перепиской в хронологическом порядке.",
        "required": [
          "uuid",
          "external_id",
          "customer_uuid",
          "number",
          "subject",
          "status",
          "priority",
          "department",
          "category",
          "service_uuid",
          "domain_uuid",
          "order_uuid",
          "last_message_at",
          "closed_at",
          "messages_count",
          "created_at",
          "updated_at",
          "messages"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "description": "UUID объекта.",
            "format": "uuid"
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "ID тикета внешнего биллинга."
          },
          "customer_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "Покупатель.",
            "format": "uuid"
          },
          "number": {
            "type": "string",
            "description": "Номер."
          },
          "subject": {
            "type": "string",
            "description": "Тема."
          },
          "status": {
            "type": "string",
            "description": "Состояние; ответ клиента открывает тикет, close_ticket=true закрывает."
          },
          "priority": {
            "type": "string",
            "description": "Приоритет.",
            "enum": [
              "low",
              "normal",
              "high",
              "urgent"
            ]
          },
          "department": {
            "type": [
              "string",
              "null"
            ],
            "description": "Отдел."
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "description": "Категория."
          },
          "service_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "Связанная услуга.",
            "format": "uuid"
          },
          "domain_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "Связанный домен.",
            "format": "uuid"
          },
          "order_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "Связанный заказ.",
            "format": "uuid"
          },
          "last_message_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Последнее сообщение.",
            "format": "date-time"
          },
          "closed_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Закрыт.",
            "format": "date-time"
          },
          "messages_count": {
            "type": "integer",
            "format": "int64",
            "description": "Количество публичных сообщений.",
            "minimum": 0
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Создан.",
            "format": "date-time"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Изменён.",
            "format": "date-time"
          },
          "messages": {
            "type": "array",
            "description": "Сообщения от старых к новым, без отдельной пагинации.",
            "items": {
              "$ref": "#/components/schemas/CoreTicketMessage"
            }
          }
        }
      },
      "CoreCreateTicketRequest": {
        "type": "object",
        "description": "Только live. У покупателя должен быть owner_user_id. Все привязанные объекты должны принадлежать этому покупателю и реселлеру.",
        "required": [
          "subject",
          "priority",
          "message"
        ],
        "properties": {
          "customer_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "Обязателен в managed.",
            "format": "uuid"
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Уникален внутри реселлера.",
            "maxLength": 255
          },
          "subject": {
            "type": "string",
            "description": "Тема.",
            "minLength": 3,
            "maxLength": 500
          },
          "priority": {
            "type": "string",
            "description": "Приоритет.",
            "enum": [
              "low",
              "normal",
              "high",
              "urgent"
            ]
          },
          "message": {
            "type": "string",
            "description": "Первое сообщение.",
            "minLength": 3,
            "maxLength": 5000
          },
          "service_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "Необязательная услуга.",
            "format": "uuid"
          },
          "domain_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "Необязательный домен.",
            "format": "uuid"
          },
          "order_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "Необязательный заказ.",
            "format": "uuid"
          }
        }
      },
      "CoreReplyTicketRequest": {
        "type": "object",
        "description": "Добавляет публичное сообщение. Нет отдельного endpoint изменения приоритета, назначения отдела или закрытия без сообщения.",
        "required": [
          "message"
        ],
        "properties": {
          "message": {
            "type": "string",
            "description": "Сообщение.",
            "minLength": 1,
            "maxLength": 5000
          },
          "close_ticket": {
            "type": "boolean",
            "description": "true закрывает тикет; false открывает его, в том числе ранее закрытый.",
            "default": false
          }
        }
      },
      "CoreContextEnvelope": {
        "type": "object",
        "description": "Успешный ответ.",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "$ref": "#/components/schemas/CoreContext"
          }
        }
      },
      "CoreCustomerEnvelope": {
        "type": "object",
        "description": "Успешный ответ.",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "$ref": "#/components/schemas/CoreCustomer"
          }
        }
      },
      "CoreContactEnvelope": {
        "type": "object",
        "description": "Успешный ответ.",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "$ref": "#/components/schemas/CoreContact"
          }
        }
      },
      "CoreContactDeletedEnvelope": {
        "type": "object",
        "description": "Успешный ответ.",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "$ref": "#/components/schemas/CoreContactDeleted"
          }
        }
      },
      "CoreQuoteEnvelope": {
        "type": "object",
        "description": "Успешный ответ.",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "$ref": "#/components/schemas/CoreQuote"
          }
        }
      },
      "CoreOrderEnvelope": {
        "type": "object",
        "description": "Успешный ответ.",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "$ref": "#/components/schemas/CoreOrder"
          }
        }
      },
      "CoreServiceEnvelope": {
        "type": "object",
        "description": "Успешный ответ.",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "$ref": "#/components/schemas/CoreService"
          }
        }
      },
      "CoreBalanceEnvelope": {
        "type": "object",
        "description": "Успешный ответ.",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "$ref": "#/components/schemas/CoreBalance"
          }
        }
      },
      "CoreTicketEnvelope": {
        "type": "object",
        "description": "Успешный ответ.",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "$ref": "#/components/schemas/CoreTicket"
          }
        }
      },
      "CoreProductPage": {
        "type": "object",
        "description": "Список и упрощённая пагинация.",
        "required": [
          "data",
          "meta"
        ],
        "properties": {
          "data": {
            "type": "array",
            "description": "Записи.",
            "items": {
              "$ref": "#/components/schemas/CoreProduct"
            }
          },
          "meta": {
            "$ref": "#/components/schemas/CoreCursorMeta"
          }
        }
      },
      "CoreZonePage": {
        "type": "object",
        "description": "Список и упрощённая пагинация.",
        "required": [
          "data",
          "meta"
        ],
        "properties": {
          "data": {
            "type": "array",
            "description": "Записи.",
            "items": {
              "$ref": "#/components/schemas/CoreZone"
            }
          },
          "meta": {
            "$ref": "#/components/schemas/CoreCursorMeta"
          }
        }
      },
      "CoreCustomerPage": {
        "type": "object",
        "description": "Список и упрощённая пагинация.",
        "required": [
          "data",
          "meta"
        ],
        "properties": {
          "data": {
            "type": "array",
            "description": "Записи.",
            "items": {
              "$ref": "#/components/schemas/CoreCustomer"
            }
          },
          "meta": {
            "$ref": "#/components/schemas/CoreCursorMeta"
          }
        }
      },
      "CoreContactPage": {
        "type": "object",
        "description": "Список и упрощённая пагинация.",
        "required": [
          "data",
          "meta"
        ],
        "properties": {
          "data": {
            "type": "array",
            "description": "Записи.",
            "items": {
              "$ref": "#/components/schemas/CoreContact"
            }
          },
          "meta": {
            "$ref": "#/components/schemas/CoreCursorMeta"
          }
        }
      },
      "CoreLedgerEntryPage": {
        "type": "object",
        "description": "Список и упрощённая пагинация.",
        "required": [
          "data",
          "meta"
        ],
        "properties": {
          "data": {
            "type": "array",
            "description": "Записи.",
            "items": {
              "$ref": "#/components/schemas/CoreLedgerEntry"
            }
          },
          "meta": {
            "$ref": "#/components/schemas/CoreCursorMeta"
          }
        }
      },
      "CoreOrderPage": {
        "type": "object",
        "description": "Список и ресурсная курсорная пагинация.",
        "required": [
          "data",
          "links",
          "meta"
        ],
        "properties": {
          "data": {
            "type": "array",
            "description": "Записи.",
            "items": {
              "$ref": "#/components/schemas/CoreOrder"
            }
          },
          "links": {
            "$ref": "#/components/schemas/CoreResourceLinks"
          },
          "meta": {
            "$ref": "#/components/schemas/CoreResourceMeta"
          }
        }
      },
      "CoreServicePage": {
        "type": "object",
        "description": "Список и ресурсная курсорная пагинация.",
        "required": [
          "data",
          "links",
          "meta"
        ],
        "properties": {
          "data": {
            "type": "array",
            "description": "Записи.",
            "items": {
              "$ref": "#/components/schemas/CoreService"
            }
          },
          "links": {
            "$ref": "#/components/schemas/CoreResourceLinks"
          },
          "meta": {
            "$ref": "#/components/schemas/CoreResourceMeta"
          }
        }
      },
      "CoreTicketSummaryPage": {
        "type": "object",
        "description": "Список и ресурсная курсорная пагинация.",
        "required": [
          "data",
          "links",
          "meta"
        ],
        "properties": {
          "data": {
            "type": "array",
            "description": "Записи.",
            "items": {
              "$ref": "#/components/schemas/CoreTicketSummary"
            }
          },
          "links": {
            "$ref": "#/components/schemas/CoreResourceLinks"
          },
          "meta": {
            "$ref": "#/components/schemas/CoreResourceMeta"
          }
        }
      },
      "DomainName": {
        "type": "string",
        "description": "Полное доменное имя без схемы, пути, порта и завершающей точки. IDN допускается; сервер нормализует регистр и преобразует в Punycode UTS46. ASCII-представление не длиннее 253, каждая метка не длиннее 63 символов. example.kz в примерах не означает доступность для регистрации.",
        "maxLength": 253,
        "examples": [
          "example.kz"
        ]
      },
      "DomainReason": {
        "type": "string",
        "description": "Причина действия для журнала аудита; не передавайте пароли или персональные данные.",
        "minLength": 3,
        "maxLength": 1000,
        "examples": [
          "Запрос клиента по заявке CRM-1001"
        ]
      },
      "DomainNameserver": {
        "type": "object",
        "required": [
          "hostname"
        ],
        "properties": {
          "hostname": {
            "$ref": "#/components/schemas/DomainName"
          },
          "ipv4": {
            "type": [
              "string",
              "null"
            ],
            "description": "Glue IPv4 или null; не адрес веб-сайта.",
            "format": "ipv4"
          },
          "ipv6": {
            "type": [
              "string",
              "null"
            ],
            "description": "Glue IPv6 или null.",
            "format": "ipv6"
          }
        }
      },
      "DomainContactSnapshot": {
        "type": "object",
        "required": [
          "uuid",
          "type",
          "status",
          "first_name",
          "last_name",
          "middle_name",
          "organization_name",
          "email",
          "phone",
          "country_code",
          "residence_country_code",
          "city",
          "region",
          "address_line",
          "postal_code",
          "external_id_type",
          "verification_status",
          "is_locked_as_registrant",
          "verified_at"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "description": "UUID локального профиля контакта, не EPP contact handle.",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "description": "Тип контакта профиля: person (физическое лицо) или organization (организация). Не путать с типом billing customer.",
            "enum": [
              "person",
              "organization"
            ]
          },
          "status": {
            "type": "string",
            "description": "Состояние локального профиля, например active."
          },
          "first_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Имя."
          },
          "last_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Фамилия."
          },
          "middle_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Отчество."
          },
          "organization_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Название организации."
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "description": "Email.",
            "format": "email"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "Телефон."
          },
          "country_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Код страны адреса."
          },
          "residence_country_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Код страны резидентства."
          },
          "city": {
            "type": [
              "string",
              "null"
            ],
            "description": "Город."
          },
          "region": {
            "type": [
              "string",
              "null"
            ],
            "description": "Регион."
          },
          "address_line": {
            "type": [
              "string",
              "null"
            ],
            "description": "Адрес."
          },
          "postal_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Почтовый индекс."
          },
          "external_id_type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Тип документа, например IIN или BIN. Сам идентификатор этот ресурс не раскрывает."
          },
          "verification_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Состояние проверки контакта."
          },
          "is_locked_as_registrant": {
            "type": "boolean",
            "description": "Контакт заблокирован как подтверждённый владелец."
          },
          "verified_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Время подтверждения или null.",
            "format": "date-time"
          }
        }
      },
      "DomainRecord": {
        "type": "object",
        "required": [
          "uuid",
          "external_id",
          "service_uuid",
          "customer_uuid",
          "domain_name",
          "unicode_domain_name",
          "punycode_domain_name",
          "status",
          "registration_status",
          "registrar_operation_status",
          "registry_status",
          "verification_status",
          "verification_registry_status",
          "edu_license_status",
          "hold_status",
          "hold_reason",
          "registered_at",
          "expires_at",
          "last_synced_at",
          "zone",
          "contacts",
          "nameservers",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "description": "UUID домена; используйте в domainUuid.",
            "format": "uuid"
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Идентификатор услуги во внешнем биллинге; null для неназначенного."
          },
          "service_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "UUID услуги для GET /services/{serviceUuid}.",
            "format": "uuid"
          },
          "customer_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "UUID плательщика.",
            "format": "uuid"
          },
          "domain_name": {
            "$ref": "#/components/schemas/DomainName"
          },
          "unicode_domain_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Unicode-представление имени."
          },
          "punycode_domain_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "ASCII/Punycode-представление имени."
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Локальное состояние услуги: например active, pending, expired. Не статус EPP."
          },
          "registration_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Локальное состояние регистрации: например pending, registered, failed."
          },
          "registrar_operation_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Последний результат операции регистратора."
          },
          "registry_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Сводное состояние реестра; строка не ограничена enum и не является массивом EPP-статусов."
          },
          "verification_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Локальное состояние проверки владельца."
          },
          "verification_registry_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Состояние проверки владельца в реестре."
          },
          "edu_license_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Состояние образовательной лицензии для соответствующих зон."
          },
          "hold_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Локальное состояние блокировки."
          },
          "hold_reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Причина блокировки; может быть null."
          },
          "registered_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Дата создания в реестре; используется для ограничения продления 24 часа.",
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Актуальное локальное время окончания; подтверждайте синхронизацией.",
            "format": "date-time"
          },
          "last_synced_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Последняя синхронизация с реестром.",
            "format": "date-time"
          },
          "zone": {
            "anyOf": [
              {
                "type": "object",
                "required": [
                  "uuid",
                  "zone",
                  "display_name"
                ],
                "properties": {
                  "uuid": {
                    "type": "string",
                    "description": "UUID зоны для расчёта quote.",
                    "format": "uuid"
                  },
                  "zone": {
                    "type": "string",
                    "description": "Зона без ведущей точки.",
                    "examples": [
                      "kz"
                    ]
                  },
                  "display_name": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Отображаемое название зоны."
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "Зона или null."
          },
          "contacts": {
            "type": "object",
            "required": [
              "owner",
              "admin",
              "tech",
              "billing"
            ],
            "properties": {
              "owner": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/DomainContactSnapshot"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Контакт роли owner или null."
              },
              "admin": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/DomainContactSnapshot"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Контакт роли admin или null."
              },
              "tech": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/DomainContactSnapshot"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Контакт роли tech или null."
              },
              "billing": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/DomainContactSnapshot"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Контакт роли billing или null."
              }
            }
          },
          "nameservers": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "hostname",
                "ipv4",
                "ipv6"
              ],
              "properties": {
                "hostname": {
                  "type": "string",
                  "description": "Имя сервера."
                },
                "ipv4": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Glue IPv4.",
                  "format": "ipv4"
                },
                "ipv6": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Glue IPv6.",
                  "format": "ipv6"
                }
              }
            },
            "description": "Текущие локальные NS; массив может быть пустым."
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Создание локальной записи.",
            "format": "date-time"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Последнее изменение локальной записи.",
            "format": "date-time"
          }
        }
      },
      "DomainEnvelope": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "$ref": "#/components/schemas/DomainRecord"
          }
        }
      },
      "DomainCursorLinks": {
        "type": "object",
        "required": [
          "first",
          "last",
          "prev",
          "next"
        ],
        "properties": {
          "first": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для cursor pagination null."
          },
          "last": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для cursor pagination null."
          },
          "prev": {
            "type": [
              "string",
              "null"
            ],
            "description": "URL предыдущей страницы либо null.",
            "format": "uri"
          },
          "next": {
            "type": [
              "string",
              "null"
            ],
            "description": "URL следующей страницы либо null.",
            "format": "uri"
          }
        }
      },
      "DomainCursorMeta": {
        "type": "object",
        "required": [
          "path",
          "per_page",
          "next_cursor",
          "prev_cursor"
        ],
        "properties": {
          "path": {
            "type": "string",
            "description": "URL списка без cursor.",
            "format": "uri"
          },
          "per_page": {
            "type": "integer",
            "description": "Фактический размер страницы.",
            "minimum": 1,
            "maximum": 100
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Непрозрачный cursor следующей страницы."
          },
          "prev_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Непрозрачный cursor предыдущей страницы."
          }
        }
      },
      "DomainListEnvelope": {
        "type": "object",
        "required": [
          "data",
          "links",
          "meta"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DomainRecord"
            },
            "description": "Домены текущего провайдера и окружения, id по убыванию."
          },
          "links": {
            "$ref": "#/components/schemas/DomainCursorLinks"
          },
          "meta": {
            "$ref": "#/components/schemas/DomainCursorMeta"
          }
        }
      },
      "DomainOperationResult": {
        "oneOf": [
          {
            "type": "array",
            "maxItems": 0,
            "description": "До завершения result сериализуется как [], а не {}."
          },
          {
            "type": "object",
            "required": [
              "domain_uuid",
              "expires_at"
            ],
            "properties": {
              "domain_uuid": {
                "type": "string",
                "description": "UUID домена.",
                "format": "uuid"
              },
              "expires_at": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Срок домена после подтверждения; null допустим.",
                "format": "date-time"
              },
              "auth_code": {
                "type": "string",
                "description": "Секрет. Только GET, action=auth_code и дополнительный scope domains.auth-code; иначе поле отсутствует. Не логировать.",
                "writeOnly": false
              }
            }
          }
        ],
        "description": "Итог не включает сырой EPP-ответ, полную запись домена или HTTP-код реестра."
      },
      "DomainOperation": {
        "type": "object",
        "required": [
          "uuid",
          "domain_uuid",
          "action",
          "status",
          "result",
          "error",
          "created_at",
          "completed_at",
          "next_check_at"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "description": "UUID задачи для опроса /operations/{operationUuid}.",
            "format": "uuid"
          },
          "domain_uuid": {
            "type": "string",
            "description": "UUID связанного домена.",
            "format": "uuid"
          },
          "action": {
            "type": "string",
            "description": "Тип операции приложения, не команда EPP.",
            "enum": [
              "renew",
              "transfer",
              "restore",
              "nameservers",
              "contacts",
              "statuses",
              "privacy",
              "sync",
              "auth_code",
              "transfer_query",
              "transfer_approve",
              "transfer_reject",
              "transfer_cancel",
              "host_create",
              "host_update",
              "host_delete",
              "delete"
            ]
          },
          "status": {
            "type": "string",
            "description": "Состояние очереди. pending: ожидает; processing: выполняется; completed: подтверждена; failed: окончательный отказ; uncertain: результат не подтверждён, выполняется сверка. Не трактовать неизвестное значение как успех.",
            "examples": [
              "pending",
              "processing",
              "completed",
              "failed",
              "uncertain"
            ]
          },
          "result": {
            "$ref": "#/components/schemas/DomainOperationResult"
          },
          "error": {
            "anyOf": [
              {
                "type": "null"
              },
              {
                "type": "object",
                "required": [
                  "code",
                  "message"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "description": "Машинный код обработки: registry_rejected, validation_failed, transfer_rejected или registry_result_unknown; новые значения допустимы."
                  },
                  "message": {
                    "type": "string",
                    "description": "Безопасное сообщение; не используйте текст как машинный код."
                  }
                }
              }
            ],
            "description": "Ошибка операции; это не HTTP ErrorEnvelope."
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Время постановки.",
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Заполняется при completed; при failed остаётся null.",
            "format": "date-time"
          },
          "next_check_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для pending/uncertain: не опрашивать чаще указанного времени; это не SLA. Для прочих статусов null.",
            "format": "date-time"
          }
        }
      },
      "DomainOperationEnvelope": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "$ref": "#/components/schemas/DomainOperation"
          }
        }
      },
      "DomainOperationListEnvelope": {
        "type": "object",
        "required": [
          "data",
          "meta"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DomainOperation"
            },
            "description": "Только reseller_domain_operation; register_domain сюда не входит."
          },
          "meta": {
            "type": "object",
            "required": [
              "next_cursor"
            ],
            "properties": {
              "next_cursor": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Следующая страница или null."
              }
            }
          }
        }
      },
      "DomainCheckRequest": {
        "type": "object",
        "required": [
          "domain"
        ],
        "properties": {
          "domain": {
            "$ref": "#/components/schemas/DomainName"
          }
        }
      },
      "DomainCheckEnvelope": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "object",
            "required": [
              "domain",
              "available",
              "reason",
              "registrar"
            ],
            "properties": {
              "domain": {
                "$ref": "#/components/schemas/DomainName"
              },
              "available": {
                "type": "boolean",
                "description": "Результат проверки в данный момент, не резервирование имени."
              },
              "reason": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Причина из драйвера; значения/язык не фиксированы."
              },
              "registrar": {
                "type": "string",
                "description": "Код регистратора выбранного маршрута."
              }
            }
          }
        }
      },
      "DomainReasonRequest": {
        "type": "object",
        "required": [
          "reason"
        ],
        "properties": {
          "reason": {
            "$ref": "#/components/schemas/DomainReason"
          }
        }
      },
      "DomainRenewRequest": {
        "type": "object",
        "required": [
          "quote_uuid",
          "reason"
        ],
        "properties": {
          "quote_uuid": {
            "type": "string",
            "description": "UUID неиспользованного, неистёкшего quote operation=renew с одним доменом; срок берётся из quote.",
            "format": "uuid"
          },
          "reason": {
            "$ref": "#/components/schemas/DomainReason"
          }
        }
      },
      "DomainTransferRequest": {
        "type": "object",
        "required": [
          "quote_uuid",
          "auth_code",
          "reason"
        ],
        "properties": {
          "quote_uuid": {
            "type": "string",
            "description": "Quote operation=transfer, quantity=1, period_unit=year, period_count=1.",
            "format": "uuid"
          },
          "auth_code": {
            "type": "string",
            "description": "Код от текущего регистратора; секрет, хранить защищённо.",
            "maxLength": 255,
            "writeOnly": true
          },
          "reason": {
            "$ref": "#/components/schemas/DomainReason"
          }
        }
      },
      "DomainIncomingTransferRequest": {
        "type": "object",
        "required": [
          "quote_uuid",
          "auth_code",
          "reason"
        ],
        "properties": {
          "quote_uuid": {
            "type": "string",
            "description": "Quote operation=transfer, quantity=1, period_unit=year, period_count=1.",
            "format": "uuid"
          },
          "auth_code": {
            "type": "string",
            "description": "Код от текущего регистратора; секрет, хранить защищённо.",
            "maxLength": 255,
            "writeOnly": true
          },
          "reason": {
            "$ref": "#/components/schemas/DomainReason"
          },
          "customer_uuid": {
            "type": [
              "string",
              "null"
            ],
            "description": "В managed обязательно передайте своего клиента; в aggregate не передавайте или укажите назначенного aggregate-клиента.",
            "format": "uuid"
          }
        }
      },
      "DomainRestoreRequest": {
        "type": "object",
        "required": [
          "quote_uuid",
          "reason"
        ],
        "properties": {
          "quote_uuid": {
            "type": "string",
            "description": "Отдельный quote operation=restore; не renew. quantity=1, year/1.",
            "format": "uuid"
          },
          "restore_auth": {
            "type": [
              "string",
              "null"
            ],
            "description": "Данные авторизации восстановления. Нужно непустое restore_auth или restore_password; формат определяется регистратором.",
            "maxLength": 255,
            "writeOnly": true
          },
          "restore_password": {
            "type": [
              "string",
              "null"
            ],
            "description": "Альтернативные данные восстановления; требования регистратора могут быть строже.",
            "maxLength": 255,
            "writeOnly": true
          },
          "reason": {
            "$ref": "#/components/schemas/DomainReason"
          }
        },
        "anyOf": [
          {
            "required": [
              "restore_auth"
            ],
            "properties": {
              "restore_auth": {
                "type": "string",
                "minLength": 1
              }
            }
          },
          {
            "required": [
              "restore_password"
            ],
            "properties": {
              "restore_password": {
                "type": "string",
                "minLength": 1
              }
            }
          }
        ]
      },
      "DomainNameserversRequest": {
        "type": "object",
        "required": [
          "nameservers",
          "reason"
        ],
        "properties": {
          "nameservers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DomainNameserver"
            },
            "description": "Полный новый набор NS, не патч; после нормализации имена должны быть различны.",
            "minItems": 2,
            "maxItems": 6
          },
          "reason": {
            "$ref": "#/components/schemas/DomainReason"
          }
        }
      },
      "DomainContactsRequest": {
        "type": "object",
        "required": [
          "contacts",
          "reason"
        ],
        "properties": {
          "contacts": {
            "type": "object",
            "required": [
              "owner",
              "admin",
              "tech",
              "billing"
            ],
            "properties": {
              "owner": {
                "type": "string",
                "description": "UUID контакта роли owner того же клиента, провайдера и окружения. Один UUID разрешён во всех ролях.",
                "format": "uuid"
              },
              "admin": {
                "type": "string",
                "description": "UUID контакта роли admin того же клиента, провайдера и окружения. Один UUID разрешён во всех ролях.",
                "format": "uuid"
              },
              "tech": {
                "type": "string",
                "description": "UUID контакта роли tech того же клиента, провайдера и окружения. Один UUID разрешён во всех ролях.",
                "format": "uuid"
              },
              "billing": {
                "type": "string",
                "description": "UUID контакта роли billing того же клиента, провайдера и окружения. Один UUID разрешён во всех ролях.",
                "format": "uuid"
              }
            },
            "additionalProperties": false
          },
          "reason": {
            "$ref": "#/components/schemas/DomainReason"
          }
        }
      },
      "DomainClientStatus": {
        "type": "string",
        "description": "Разрешённый устанавливаемый client-статус. Статусы server* через этот API не меняются.",
        "enum": [
          "clientTransferProhibited",
          "clientUpdateProhibited",
          "clientDeleteProhibited",
          "clientRenewProhibited",
          "clientHold"
        ]
      },
      "DomainStatusesRequest": {
        "type": "object",
        "required": [
          "reason"
        ],
        "properties": {
          "add": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DomainClientStatus"
            },
            "description": "Добавляемые статусы; уникальны, не пересекаются с remove.",
            "maxItems": 5,
            "uniqueItems": true
          },
          "remove": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DomainClientStatus"
            },
            "description": "Удаляемые статусы; уникальны.",
            "maxItems": 5,
            "uniqueItems": true
          },
          "reason": {
            "$ref": "#/components/schemas/DomainReason"
          }
        },
        "anyOf": [
          {
            "required": [
              "add"
            ],
            "properties": {
              "add": {
                "minItems": 1
              }
            }
          },
          {
            "required": [
              "remove"
            ],
            "properties": {
              "remove": {
                "minItems": 1
              }
            }
          }
        ]
      },
      "DomainPrivacyField": {
        "type": "string",
        "description": "Поле раскрытия WHOIS.",
        "enum": [
          "name",
          "organization",
          "address",
          "phone",
          "fax",
          "email"
        ]
      },
      "DomainPrivacyRequest": {
        "type": "object",
        "required": [
          "enabled",
          "reason"
        ],
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Включить или выключить скрытие."
          },
          "hidden_fields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DomainPrivacyField"
            },
            "description": "Необязательный список скрываемых полей. При пропуске используются настройки менеджера; смотрите capabilities.whois_privacy.",
            "uniqueItems": true
          },
          "reason": {
            "$ref": "#/components/schemas/DomainReason"
          }
        }
      },
      "DomainHostCreateRequest": {
        "type": "object",
        "required": [
          "hostname",
          "reason"
        ],
        "properties": {
          "hostname": {
            "$ref": "#/components/schemas/DomainName"
          },
          "ipv4": {
            "type": [
              "string",
              "null"
            ],
            "description": "IPv4 subordinate host; обязателен, если ipv6 отсутствует.",
            "format": "ipv4"
          },
          "ipv6": {
            "type": [
              "string",
              "null"
            ],
            "description": "IPv6 subordinate host; обязателен, если ipv4 отсутствует.",
            "format": "ipv6"
          },
          "reason": {
            "$ref": "#/components/schemas/DomainReason"
          }
        },
        "anyOf": [
          {
            "required": [
              "ipv4"
            ],
            "properties": {
              "ipv4": {
                "type": "string",
                "format": "ipv4"
              }
            }
          },
          {
            "required": [
              "ipv6"
            ],
            "properties": {
              "ipv6": {
                "type": "string",
                "format": "ipv6"
              }
            }
          }
        ]
      },
      "DomainHostUpdateRequest": {
        "type": "object",
        "required": [
          "hostname",
          "reason"
        ],
        "properties": {
          "hostname": {
            "$ref": "#/components/schemas/DomainName"
          },
          "add_addresses": {
            "type": "array",
            "items": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "ipv4"
                },
                {
                  "type": "string",
                  "format": "ipv6"
                }
              ],
              "description": "IPv4 или IPv6."
            },
            "description": "Добавить адреса; не пересекаются с remove_addresses после бинарной нормализации IP.",
            "maxItems": 10,
            "uniqueItems": true
          },
          "remove_addresses": {
            "type": "array",
            "items": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "ipv4"
                },
                {
                  "type": "string",
                  "format": "ipv6"
                }
              ],
              "description": "IPv4 или IPv6."
            },
            "description": "Удалить адреса.",
            "maxItems": 10,
            "uniqueItems": true
          },
          "reason": {
            "$ref": "#/components/schemas/DomainReason"
          }
        },
        "anyOf": [
          {
            "required": [
              "add_addresses"
            ],
            "properties": {
              "add_addresses": {
                "minItems": 1
              }
            }
          },
          {
            "required": [
              "remove_addresses"
            ],
            "properties": {
              "remove_addresses": {
                "minItems": 1
              }
            }
          }
        ]
      },
      "DomainHostDeleteRequest": {
        "type": "object",
        "required": [
          "hostname",
          "reason"
        ],
        "properties": {
          "hostname": {
            "$ref": "#/components/schemas/DomainName"
          },
          "reason": {
            "$ref": "#/components/schemas/DomainReason"
          }
        }
      },
      "DomainDeleteRequest": {
        "type": "object",
        "required": [
          "confirm_domain",
          "reason"
        ],
        "properties": {
          "confirm_domain": {
            "type": "string",
            "description": "Точное значение domain_name из GET домена; сравнение регистрозависимое, без IDN-нормализации.",
            "maxLength": 253,
            "examples": [
              "example.kz"
            ]
          },
          "reason": {
            "$ref": "#/components/schemas/DomainReason"
          }
        }
      },
      "DomainAutoRenewRequest": {
        "type": "object",
        "required": [
          "enabled",
          "reason"
        ],
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Согласие на автопродление на 1 год по актуальному тарифу; false отключает."
          },
          "reason": {
            "$ref": "#/components/schemas/DomainReason"
          }
        }
      },
      "DomainAutoRenewEnvelope": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "object",
            "required": [
              "domain_uuid",
              "auto_renew",
              "period_years",
              "days_before_expiry"
            ],
            "properties": {
              "domain_uuid": {
                "type": "string",
                "description": "UUID домена.",
                "format": "uuid"
              },
              "auto_renew": {
                "type": "boolean",
                "description": "Сохранённое согласие."
              },
              "period_years": {
                "type": "integer",
                "const": 1,
                "description": "Срок автоматического продления."
              },
              "days_before_expiry": {
                "type": "integer",
                "const": 7,
                "description": "Окно, в котором планировщик рассматривает продление."
              }
            }
          }
        }
      },
      "DomainHostInfoEnvelope": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "object",
            "required": [
              "hostname",
              "exists",
              "status",
              "addresses",
              "statuses",
              "createdAt",
              "updatedAt"
            ],
            "properties": {
              "hostname": {
                "type": "string",
                "description": "Имя host-объекта."
              },
              "exists": {
                "type": "boolean",
                "description": "Существует ли объект в реестре."
              },
              "status": {
                "type": "string",
                "description": "Результат операции драйвера; строка, не закрытый enum."
              },
              "addresses": {
                "type": "array",
                "items": {
                  "oneOf": [
                    {
                      "type": "string",
                      "format": "ipv4"
                    },
                    {
                      "type": "string",
                      "format": "ipv6"
                    }
                  ],
                  "description": "IPv4 или IPv6."
                },
                "description": "Адреса host-объекта."
              },
              "statuses": {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "Статус EPP без фиксированного enum."
                },
                "description": "Статусы host-объекта."
              },
              "createdAt": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Дата создания в реестре. Именно camelCase.",
                "format": "date-time"
              },
              "updatedAt": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Дата изменения в реестре. Именно camelCase.",
                "format": "date-time"
              }
            }
          }
        }
      },
      "DomainCapabilitiesEnvelope": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "object",
            "required": [
              "operations",
              "renewal_available_at",
              "maximum_term_years",
              "auto_renew",
              "whois_privacy"
            ],
            "properties": {
              "operations": {
                "type": "object",
                "required": [
                  "renew",
                  "transfer",
                  "transfer_query",
                  "transfer_approve",
                  "transfer_reject",
                  "transfer_cancel",
                  "restore",
                  "nameservers",
                  "contacts",
                  "statuses",
                  "delete",
                  "auth_code",
                  "privacy",
                  "sync",
                  "host_create",
                  "host_update",
                  "host_delete",
                  "host_info",
                  "dnssec"
                ],
                "properties": {
                  "renew": {
                    "type": "boolean",
                    "description": "Поддержка renew драйвером при активной интеграции и отсутствии migration hold. Не гарантирует достаточный scope/баланс/допустимость текущего состояния."
                  },
                  "transfer": {
                    "type": "boolean",
                    "description": "Поддержка transfer драйвером при активной интеграции и отсутствии migration hold. Не гарантирует достаточный scope/баланс/допустимость текущего состояния."
                  },
                  "transfer_query": {
                    "type": "boolean",
                    "description": "Поддержка transfer_query драйвером при активной интеграции и отсутствии migration hold. Не гарантирует достаточный scope/баланс/допустимость текущего состояния."
                  },
                  "transfer_approve": {
                    "type": "boolean",
                    "description": "Поддержка transfer_approve драйвером при активной интеграции и отсутствии migration hold. Не гарантирует достаточный scope/баланс/допустимость текущего состояния."
                  },
                  "transfer_reject": {
                    "type": "boolean",
                    "description": "Поддержка transfer_reject драйвером при активной интеграции и отсутствии migration hold. Не гарантирует достаточный scope/баланс/допустимость текущего состояния."
                  },
                  "transfer_cancel": {
                    "type": "boolean",
                    "description": "Поддержка transfer_cancel драйвером при активной интеграции и отсутствии migration hold. Не гарантирует достаточный scope/баланс/допустимость текущего состояния."
                  },
                  "restore": {
                    "type": "boolean",
                    "description": "Поддержка restore драйвером при активной интеграции и отсутствии migration hold. Не гарантирует достаточный scope/баланс/допустимость текущего состояния."
                  },
                  "nameservers": {
                    "type": "boolean",
                    "description": "Поддержка nameservers драйвером при активной интеграции и отсутствии migration hold. Не гарантирует достаточный scope/баланс/допустимость текущего состояния."
                  },
                  "contacts": {
                    "type": "boolean",
                    "description": "Поддержка contacts драйвером при активной интеграции и отсутствии migration hold. Не гарантирует достаточный scope/баланс/допустимость текущего состояния."
                  },
                  "statuses": {
                    "type": "boolean",
                    "description": "Поддержка statuses драйвером при активной интеграции и отсутствии migration hold. Не гарантирует достаточный scope/баланс/допустимость текущего состояния."
                  },
                  "delete": {
                    "type": "boolean",
                    "description": "Поддержка delete драйвером при активной интеграции и отсутствии migration hold. Не гарантирует достаточный scope/баланс/допустимость текущего состояния."
                  },
                  "auth_code": {
                    "type": "boolean",
                    "description": "Поддержка auth_code драйвером при активной интеграции и отсутствии migration hold. Не гарантирует достаточный scope/баланс/допустимость текущего состояния."
                  },
                  "privacy": {
                    "type": "boolean",
                    "description": "Поддержка privacy драйвером при активной интеграции и отсутствии migration hold. Не гарантирует достаточный scope/баланс/допустимость текущего состояния."
                  },
                  "sync": {
                    "type": "boolean",
                    "description": "Поддержка sync драйвером при активной интеграции и отсутствии migration hold. Не гарантирует достаточный scope/баланс/допустимость текущего состояния."
                  },
                  "host_create": {
                    "type": "boolean",
                    "description": "Поддержка host_create драйвером при активной интеграции и отсутствии migration hold. Не гарантирует достаточный scope/баланс/допустимость текущего состояния."
                  },
                  "host_update": {
                    "type": "boolean",
                    "description": "Поддержка host_update драйвером при активной интеграции и отсутствии migration hold. Не гарантирует достаточный scope/баланс/допустимость текущего состояния."
                  },
                  "host_delete": {
                    "type": "boolean",
                    "description": "Поддержка host_delete драйвером при активной интеграции и отсутствии migration hold. Не гарантирует достаточный scope/баланс/допустимость текущего состояния."
                  },
                  "host_info": {
                    "type": "boolean",
                    "description": "Поддержка host_info драйвером при активной интеграции и отсутствии migration hold. Не гарантирует достаточный scope/баланс/допустимость текущего состояния."
                  },
                  "dnssec": {
                    "type": "boolean",
                    "description": "Всегда false: DNSSEC не реализован в текущем контракте."
                  }
                }
              },
              "renewal_available_at": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "registered_at + 24 часа; null, если дата регистрации неизвестна. Дополнительные ограничения продления сохраняются.",
                "format": "date-time"
              },
              "maximum_term_years": {
                "type": "integer",
                "const": 10,
                "description": "Предельный суммарный оставшийся срок: новая expires_at не позднее текущего времени + 10 календарных лет."
              },
              "auto_renew": {
                "type": "boolean",
                "description": "Согласие на автопродление."
              },
              "whois_privacy": {
                "type": "object",
                "required": [
                  "enabled",
                  "status",
                  "hidden_fields",
                  "available_fields",
                  "last_applied_at"
                ],
                "properties": {
                  "enabled": {
                    "type": "boolean",
                    "description": "Локальное включение WHOIS privacy."
                  },
                  "status": {
                    "type": "string",
                    "description": "Состояние применения; строка без закрытого enum."
                  },
                  "hidden_fields": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/DomainPrivacyField"
                    },
                    "description": "Скрываемые поля."
                  },
                  "available_fields": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "code",
                        "label",
                        "registrar_field",
                        "description"
                      ],
                      "properties": {
                        "code": {
                          "$ref": "#/components/schemas/DomainPrivacyField"
                        },
                        "label": {
                          "type": "string",
                          "description": "Локализованное название."
                        },
                        "registrar_field": {
                          "type": "string",
                          "description": "XML-поле регистратора."
                        },
                        "description": {
                          "type": "string",
                          "description": "Локализованное пояснение."
                        }
                      }
                    },
                    "description": "Поддерживаемые поля политики раскрытия."
                  },
                  "last_applied_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Последнее применение либо null.",
                    "format": "date-time"
                  }
                }
              }
            }
          }
        }
      },
      "DomainVerificationSnapshot": {
        "type": "object",
        "required": [
          "domain-name",
          "domain-creation-time",
          "registrant-name",
          "registrant-org",
          "registrant-residencedetails-country",
          "registrant-residencedetails-externalidtype",
          "registrant-residencedetails-externalidvalue"
        ],
        "properties": {
          "domain-name": {
            "type": "string",
            "description": "Точное имя из реестра, Punycode."
          },
          "domain-creation-time": {
            "type": [
              "string",
              "null"
            ],
            "description": "Дата создания в EPP, сохраняйте исходную строку."
          },
          "registrant-name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Имя владельца из реестра/манифеста."
          },
          "registrant-org": {
            "type": [
              "string",
              "null"
            ],
            "description": "Организация владельца либо null."
          },
          "registrant-residencedetails-country": {
            "type": [
              "string",
              "null"
            ],
            "description": "Страна резидентства."
          },
          "registrant-residencedetails-externalidtype": {
            "type": [
              "string",
              "null"
            ],
            "description": "Тип документа, например IIN/BIN."
          },
          "registrant-residencedetails-externalidvalue": {
            "type": [
              "string",
              "null"
            ],
            "description": "Чувствительный идентификатор владельца; не логируйте."
          }
        }
      },
      "DomainSignatureOptions": {
        "type": "object",
        "required": [
          "method",
          "format",
          "decode",
          "encapsulate",
          "digested",
          "timestamp_applied",
          "cms_type",
          "cades_profile"
        ],
        "properties": {
          "method": {
            "type": "string",
            "description": "Для действительной подписи строго kz.gov.pki.knca.basics.sign.",
            "maxLength": 80,
            "examples": [
              "kz.gov.pki.knca.basics.sign"
            ]
          },
          "format": {
            "type": "string",
            "description": "Для действительной подписи строго cms.",
            "maxLength": 20,
            "examples": [
              "cms"
            ]
          },
          "decode": {
            "type": "boolean",
            "const": false,
            "description": "Подписываются исходные текстовые байты, не Base64-декодированный документ."
          },
          "encapsulate": {
            "type": "boolean",
            "const": false,
            "description": "Только detached CMS."
          },
          "digested": {
            "type": "boolean",
            "const": false,
            "description": "Не передавать предварительный дайджест вместо документа."
          },
          "timestamp_applied": {
            "type": "boolean",
            "const": true,
            "description": "Нужна метка времени CAdES-T."
          },
          "cms_type": {
            "type": "string",
            "description": "Для действительной подписи строго CMS Detached.",
            "maxLength": 80,
            "examples": [
              "CMS Detached"
            ]
          },
          "cades_profile": {
            "type": "string",
            "description": "Для действительной подписи строго CAdES-T.",
            "maxLength": 80,
            "examples": [
              "CAdES-T"
            ]
          }
        }
      },
      "DomainVerificationPayloadEnvelope": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "object",
            "required": [
              "type",
              "eds_provider",
              "doc_spec_alg",
              "signer_role",
              "payload_hash",
              "signable_payload",
              "payload_snapshot",
              "issued_at",
              "expires_at",
              "ncalayer_options",
              "signature_options"
            ],
            "properties": {
              "type": {
                "type": "string",
                "const": "eds",
                "description": "Поддерживаемый способ."
              },
              "eds_provider": {
                "type": "string",
                "const": "ncalayer",
                "description": "Провайдер подписи."
              },
              "doc_spec_alg": {
                "type": "string",
                "const": "DOC-SPEC-REGISTRANT-IDENTITY-CONFIRMATION-V1",
                "description": "Версия подписываемого документа."
              },
              "signer_role": {
                "type": "string",
                "const": "CURRENT_REGISTRANT",
                "description": "Текущий владелец домена."
              },
              "payload_hash": {
                "type": "string",
                "description": "SHA-256 точной строки signable_payload.",
                "pattern": "^[a-f0-9]{64}$"
              },
              "signable_payload": {
                "type": "string",
                "description": "Полный двуязычный документ. Подписывайте без изменения пробелов, переносов, кодировки UTF-8 и порядка строк."
              },
              "payload_snapshot": {
                "$ref": "#/components/schemas/DomainVerificationSnapshot"
              },
              "issued_at": {
                "type": "string",
                "description": "Время формирования.",
                "format": "date-time"
              },
              "expires_at": {
                "type": "string",
                "description": "Рекомендуемое время обновления документа (issued_at + 15 минут). При submit сервер сверяет актуальные данные и точный текст, но отдельный timestamp/token срока не принимает.",
                "format": "date-time"
              },
              "ncalayer_options": {
                "type": "object",
                "required": [
                  "environment",
                  "allowedStorages",
                  "locale",
                  "signerParams"
                ],
                "properties": {
                  "environment": {
                    "type": "string",
                    "description": "Окружение NCALayer, test или production."
                  },
                  "allowedStorages": {
                    "anyOf": [
                      {
                        "type": "array",
                        "items": {
                          "type": "string",
                          "description": "Идентификатор хранилища."
                        },
                        "description": "В test [PKCS12]."
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "Разрешённые хранилища или null в production."
                  },
                  "locale": {
                    "type": "string",
                    "description": "Локаль NCALayer."
                  },
                  "signerParams": {
                    "type": "object",
                    "required": [
                      "extKeyUsageOids",
                      "chain"
                    ],
                    "properties": {
                      "extKeyUsageOids": {
                        "type": "array",
                        "items": {
                          "type": "string",
                          "description": "OID назначения ключа."
                        },
                        "description": "Ограничения назначения сертификата."
                      },
                      "chain": {
                        "anyOf": [
                          {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "description": "Сертификат цепочки."
                            },
                            "description": "Тестовая цепочка."
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "Цепочка сертификатов или null."
                      }
                    }
                  }
                }
              },
              "signature_options": {
                "$ref": "#/components/schemas/DomainSignatureOptions"
              }
            }
          }
        }
      },
      "DomainVerificationRequest": {
        "type": "object",
        "required": [
          "type",
          "payload_snapshot",
          "signable_payload"
        ],
        "properties": {
          "type": {
            "type": "string",
            "description": "На уровне HTTP принимается строка до 80; фактически поддерживается только eds. Другой тип создаёт failed Verification.",
            "maxLength": 80,
            "examples": [
              "eds"
            ]
          },
          "payload_snapshot": {
            "$ref": "#/components/schemas/DomainVerificationSnapshot",
            "description": "Сервер HTTP проверяет array; успешная бизнес-проверка требует точные семь ключей из GET. Не добавляйте/не удаляйте поля."
          },
          "signable_payload": {
            "type": "string",
            "description": "Скопируйте полный signable_payload из свежего GET без изменений."
          },
          "signature": {
            "type": [
              "string",
              "null"
            ],
            "description": "Base64 DER detached CMS CAdES-T, обязательна при type=eds. Пример ниже не является действительной подписью.",
            "writeOnly": true
          },
          "certificate": {
            "type": [
              "string",
              "null"
            ],
            "description": "Необязательный сертификат подписанта.",
            "writeOnly": true
          },
          "eds_provider": {
            "type": [
              "string",
              "null"
            ],
            "description": "Идентификатор провайдера; рекомендуется ncalayer.",
            "maxLength": 80
          },
          "signature_options": {
            "$ref": "#/components/schemas/DomainSignatureRequestOptions"
          }
        },
        "allOf": [
          {
            "if": {
              "properties": {
                "type": {
                  "const": "eds"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "required": [
                "signature",
                "signature_options"
              ],
              "properties": {
                "signature": {
                  "type": "string",
                  "minLength": 1
                },
                "signature_options": {
                  "type": "object",
                  "minProperties": 1
                }
              }
            }
          }
        ]
      },
      "DomainVerificationEnvelope": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "object",
            "required": [
              "uuid",
              "type",
              "status",
              "eds_provider",
              "verification_status",
              "verified_at",
              "failed_at",
              "expires_at",
              "failure_message"
            ],
            "properties": {
              "uuid": {
                "type": "string",
                "description": "UUID попытки проверки.",
                "format": "uuid"
              },
              "type": {
                "type": "string",
                "description": "Способ проверки, например eds."
              },
              "status": {
                "type": "string",
                "description": "Состояние попытки; как минимум pending, verified, failed. HTTP 201 не означает verified."
              },
              "eds_provider": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Провайдер подписи."
              },
              "verification_status": {
                "type": "string",
                "description": "Дублирует status этой попытки."
              },
              "verified_at": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Время успешной проверки или null.",
                "format": "date-time"
              },
              "failed_at": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Время отказа или null.",
                "format": "date-time"
              },
              "expires_at": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Срок действия подтверждения либо null; не путать со сроком домена/документа.",
                "format": "date-time"
              },
              "failure_message": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Локализованная причина отказа; отдельный failure_code в этом ресурсе не выдаётся."
              }
            }
          }
        }
      },
      "DomainSignatureRequestOptions": {
        "type": "object",
        "description": "HTTP-валидатор допускает неполный объект/nullable-поля. Для действительной EDS-проверки передайте ВСЕ восемь значений из signature_options GET; иначе возможен HTTP 201 со status=failed.",
        "properties": {
          "method": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для действительной подписи строго kz.gov.pki.knca.basics.sign.",
            "maxLength": 80,
            "examples": [
              "kz.gov.pki.knca.basics.sign"
            ]
          },
          "format": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для действительной подписи строго cms.",
            "maxLength": 20,
            "examples": [
              "cms"
            ]
          },
          "decode": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Подписываются исходные текстовые байты, не Base64-декодированный документ."
          },
          "encapsulate": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Только detached CMS."
          },
          "digested": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Не передавать предварительный дайджест вместо документа."
          },
          "timestamp_applied": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Нужна метка времени CAdES-T."
          },
          "cms_type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для действительной подписи строго CMS Detached.",
            "maxLength": 80,
            "examples": [
              "CMS Detached"
            ]
          },
          "cades_profile": {
            "type": [
              "string",
              "null"
            ],
            "description": "Для действительной подписи строго CAdES-T.",
            "maxLength": 80,
            "examples": [
              "CAdES-T"
            ]
          }
        }
      },
      "WebhookEventType": {
        "type": "string",
        "enum": [
          "customer.created",
          "customer.updated",
          "domain.created",
          "domain.registration.updated",
          "domain.operation.updated",
          "domain.updated",
          "domain.nameservers.updated",
          "domain.contacts.updated",
          "domain.verification.submitted",
          "domain.verification.updated",
          "domain.renewed",
          "domain.auto_renew.failed",
          "domain.transfer.requested",
          "domain.transfer.updated",
          "hosting.created",
          "hosting.updated",
          "order.created",
          "order.updated",
          "invoice.created",
          "invoice.updated",
          "payment.completed",
          "service.updated",
          "ticket.created",
          "ticket.updated",
          "webhook.test"
        ],
        "description": "Допустимые подписки. Девять имён зарезервированы без действующих отправителей: domain.nameservers.updated, domain.contacts.updated, domain.renewed, domain.transfer.requested, hosting.created, hosting.updated, invoice.created, invoice.updated, payment.completed. Не ждите их для завершения операций."
      },
      "WebhookCreate": {
        "type": "object",
        "description": "Создание активной подписки. environment берётся из Bearer, status передавать запрещено.",
        "required": [
          "name",
          "url",
          "event_subscriptions"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 2,
            "maxLength": 255,
            "description": "Название для различения получателей."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Публичный HTTPS URL. Все DNS A/AAAA должны быть глобальными адресами. Запрещены localhost, private/reserved/multicast/NAT64, userinfo, fragment. Redirect не выполняется. Сертификат и hostname проверяются."
          },
          "event_subscriptions": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "description": "Точные имена, не wildcard. Дубликаты удаляются при сохранении. Регистрация имени не гарантирует наличие отправителя."
          },
          "timeout_seconds": {
            "type": "integer",
            "minimum": 1,
            "maximum": 30,
            "default": 10,
            "description": "Общий timeout одной попытки в секундах. Default 10, может изменяться провайдером. Connect timeout min(5,timeout_seconds)."
          },
          "max_attempts": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20,
            "default": 8,
            "description": "Максимум попыток, включая первую, по умолчанию 8 (настройка провайдера). После исчерпания статус failed, не бесконечные повторы."
          }
        }
      },
      "WebhookUpdate": {
        "type": "object",
        "description": "Частичное изменение. Пропущенные поля сохраняются; null для перечисленных полей запрещён. Пустой объект допустим, но выполняется проверка URL. Для отключения достаточно status=disabled.",
        "required": [],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 2,
            "maxLength": 255,
            "description": "Название для различения получателей."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Публичный HTTPS URL. Все DNS A/AAAA должны быть глобальными адресами. Запрещены localhost, private/reserved/multicast/NAT64, userinfo, fragment. Redirect не выполняется. Сертификат и hostname проверяются."
          },
          "event_subscriptions": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "description": "Точные имена, не wildcard. Дубликаты удаляются при сохранении. Регистрация имени не гарантирует наличие отправителя."
          },
          "timeout_seconds": {
            "type": "integer",
            "minimum": 1,
            "maximum": 30,
            "default": 10,
            "description": "Общий timeout одной попытки в секундах. Default 10, может изменяться провайдером. Connect timeout min(5,timeout_seconds)."
          },
          "max_attempts": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20,
            "default": 8,
            "description": "Максимум попыток, включая первую, по умолчанию 8 (настройка провайдера). После исчерпания статус failed, не бесконечные повторы."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "disabled"
            ],
            "description": "Выключение не удаляет настройки. Повторное включение не возвращает автоматически пропущенные события."
          }
        }
      },
      "Webhook": {
        "type": "object",
        "description": "Настройки webhook; signing_secret здесь никогда не возвращается.",
        "required": [
          "uuid",
          "environment",
          "name",
          "url",
          "event_subscriptions",
          "timeout_seconds",
          "max_attempts",
          "status",
          "rotated_at",
          "disabled_at",
          "created_at"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid",
            "description": "UUID подписки."
          },
          "environment": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ],
            "description": "Среда подписки; события другой среды не доставляются."
          },
          "name": {
            "type": "string",
            "minLength": 2,
            "maxLength": 255,
            "description": "Название для различения получателей."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Публичный HTTPS URL. Все DNS A/AAAA должны быть глобальными адресами. Запрещены localhost, private/reserved/multicast/NAT64, userinfo, fragment. Redirect не выполняется. Сертификат и hostname проверяются."
          },
          "event_subscriptions": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "description": "Точные имена, не wildcard. Дубликаты удаляются при сохранении. Регистрация имени не гарантирует наличие отправителя."
          },
          "timeout_seconds": {
            "type": "integer",
            "minimum": 1,
            "maximum": 30,
            "default": 10,
            "description": "Общий timeout одной попытки в секундах. Default 10, может изменяться провайдером. Connect timeout min(5,timeout_seconds)."
          },
          "max_attempts": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20,
            "default": 8,
            "description": "Максимум попыток, включая первую, по умолчанию 8 (настройка провайдера). После исчерпания статус failed, не бесконечные повторы."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "disabled"
            ],
            "description": "Активность получателя."
          },
          "rotated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Последняя ротация секрета, null до первой ротации."
          },
          "disabled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Последнее отключение; null для активного."
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Создание подписки."
          }
        }
      },
      "WebhookResponse": {
        "type": "object",
        "description": "Одна подписка.",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "$ref": "#/components/schemas/Webhook"
          }
        }
      },
      "WebhookSecretResponse": {
        "type": "object",
        "description": "Создание/ротация возвращает секрет. Последующие GET его не возвращают; HTTP replay с тем же ключом в пределах TTL может повторить этот ответ, включая секрет.",
        "required": [
          "data",
          "signing_secret",
          "secret_notice"
        ],
        "properties": {
          "data": {
            "$ref": "#/components/schemas/Webhook"
          },
          "signing_secret": {
            "type": "string",
            "pattern": "^whsec_[A-Za-z0-9]{64}$",
            "writeOnly": false,
            "description": "Общий секрет HMAC, whsec_ плюс 64 символа. Храните зашифрованно; не публикуйте в браузере/логах/ИИ."
          },
          "secret_notice": {
            "type": "string",
            "const": "This signing secret is shown once and cannot be recovered.",
            "description": "Текст предупреждения. Ограничение относится к GET; учтите HTTP replay."
          }
        }
      },
      "WebhookList": {
        "type": "object",
        "description": "Страница подписок, новые сначала.",
        "required": [
          "data",
          "meta"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Webhook"
            },
            "description": "Подписки текущего реселлера и среды; общие для его ключей с нужным scope."
          },
          "meta": {
            "$ref": "#/components/schemas/CursorMeta"
          }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "description": "Текущее состояние доставки. Это не неизменяемый журнал каждой попытки; redeliver сбрасывает счётчик и поля результата той же доставки.",
        "required": [
          "uuid",
          "event_uuid",
          "event_type",
          "status",
          "attempt_number",
          "http_status",
          "error_code",
          "next_attempt_at",
          "delivered_at",
          "failed_at"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid",
            "description": "UUID доставки. Не входит в тело исходящего webhook."
          },
          "event_uuid": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "ID события для дедупликации; null если связь недоступна."
          },
          "event_type": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/WebhookEventType"
              },
              {
                "type": "null"
              }
            ],
            "description": "Имя события."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "retry_scheduled",
              "delivered",
              "failed",
              "cancelled"
            ],
            "description": "pending: ждёт; retry_scheduled: назначен повтор; delivered: 2xx; failed: исчерпаны попытки; cancelled: отключение/нарушение контекста."
          },
          "attempt_number": {
            "type": "integer",
            "minimum": 0,
            "description": "Завершённые попытки в текущем цикле, начинается с 0; redeliver сбрасывает."
          },
          "http_status": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 100,
            "maximum": 599,
            "description": "Последний HTTP статус, null до ответа или при transport_error."
          },
          "error_code": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              null,
              "http_error",
              "transport_error",
              "webhook_disabled",
              "delivery_scope_mismatch"
            ],
            "description": "Не HTTP error.code. http_error: не-2xx; transport_error: DNS/TLS/connect/timeout/URL; отмена: webhook_disabled/delivery_scope_mismatch."
          },
          "next_attempt_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Не раньше какого времени назначена попытка; не обещание точного времени."
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Когда принят 2xx; иначе null."
          },
          "failed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Время failed/cancelled; иначе null."
          }
        }
      },
      "WebhookDeliveryResponse": {
        "type": "object",
        "description": "Одна доставка, принятая в очередь; 202 не доказывает получение события.",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "$ref": "#/components/schemas/WebhookDelivery"
          }
        }
      },
      "WebhookDeliveryList": {
        "type": "object",
        "description": "Страница доставок, новые сначала. Тела HTTP ответов и response_excerpt не раскрываются.",
        "required": [
          "data",
          "meta"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookDelivery"
            }
          },
          "meta": {
            "$ref": "#/components/schemas/CursorMeta"
          }
        }
      },
      "WebhookTestData": {
        "type": "object",
        "description": "Тестовое событие, отправляется даже если webhook.test не выбран в подписках.",
        "required": [
          "webhook_uuid"
        ],
        "properties": {
          "webhook_uuid": {
            "type": "string",
            "format": "uuid",
            "description": "Подписка, выбранная для теста."
          }
        }
      },
      "WebhookCustomerData": {
        "type": "object",
        "description": "Создание/изменение клиента.",
        "required": [
          "customer_uuid",
          "external_id"
        ],
        "properties": {
          "customer_uuid": {
            "type": "string",
            "format": "uuid",
            "description": "Клиент."
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Внешний идентификатор либо null."
          }
        }
      },
      "WebhookContactData": {
        "type": "object",
        "description": "customer.updated также уведомляет об изменении контакта.",
        "required": [
          "customer_uuid",
          "contact_uuid",
          "action"
        ],
        "properties": {
          "customer_uuid": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Владелец контакта."
          },
          "contact_uuid": {
            "type": "string",
            "format": "uuid",
            "description": "Контакт."
          },
          "action": {
            "type": "string",
            "enum": [
              "contact_created",
              "contact_updated",
              "contact_deleted"
            ],
            "description": "Причина customer.updated."
          }
        }
      },
      "WebhookTransferState": {
        "type": "object",
        "description": "Минимальное состояние переноса, без auth code.",
        "required": [
          "status",
          "registry_transfer_status"
        ],
        "properties": {
          "status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Внутреннее состояние переноса, null если отсутствует."
          },
          "registry_transfer_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Состояние из реестра, null если неизвестно."
          }
        }
      },
      "WebhookDomainState": {
        "type": "object",
        "description": "Снимок домена при создании, обновлении, изменении регистрации/проверок/переноса; не полная карточка.",
        "required": [
          "domain_uuid",
          "external_id",
          "status",
          "deleted",
          "domain_name",
          "registration_status",
          "verification_status",
          "edu_license_status",
          "registry_status",
          "expires_at",
          "registrar_operation_status",
          "verification_registry_status",
          "edu_license_registry_status",
          "hold_status",
          "transfer"
        ],
        "properties": {
          "domain_uuid": {
            "type": "string",
            "format": "uuid",
            "description": "Домен."
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Внешний ID."
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Локальный статус домена."
          },
          "deleted": {
            "type": "boolean",
            "description": "Локальная запись помечена удалённой; не самостоятельное доказательство освобождения имени."
          },
          "domain_name": {
            "type": "string",
            "description": "Имя домена."
          },
          "registration_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Состояние регистрации."
          },
          "verification_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Состояние проверки владельца."
          },
          "edu_license_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Состояние лицензии .edu.kz."
          },
          "registry_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Сводный статус реестра, не массив EPP-статусов; null если неизвестен."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Срок окончания по доступному состоянию."
          },
          "registrar_operation_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Состояние последней операции реестра."
          },
          "verification_registry_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Результат проверки владельца в реестре."
          },
          "edu_license_registry_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Результат проверки лицензии в реестре."
          },
          "hold_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Локальное ограничение."
          },
          "transfer": {
            "$ref": "#/components/schemas/WebhookTransferState"
          }
        }
      },
      "WebhookOperationData": {
        "type": "object",
        "description": "Изменение асинхронной доменной операции. Детали результата/ошибки получайте GET operations/{operationUuid}.",
        "required": [
          "operation_uuid",
          "domain_uuid",
          "action",
          "status"
        ],
        "properties": {
          "operation_uuid": {
            "type": "string",
            "format": "uuid",
            "description": "Операция."
          },
          "domain_uuid": {
            "type": "string",
            "format": "uuid",
            "description": "Домен."
          },
          "action": {
            "type": "string",
            "enum": [
              "renew",
              "transfer",
              "restore",
              "nameservers",
              "contacts",
              "statuses",
              "privacy",
              "sync",
              "auth_code",
              "transfer_query",
              "transfer_approve",
              "transfer_reject",
              "transfer_cancel",
              "host_create",
              "host_update",
              "host_delete",
              "delete"
            ],
            "description": "Действие."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "processing",
              "uncertain",
              "completed",
              "failed"
            ],
            "description": "Статус REST-задачи; uncertain не равен failed. awaiting_registry здесь не выставляется текущим обработчиком."
          }
        }
      },
      "WebhookVerificationData": {
        "type": "object",
        "description": "Заявка проверки владельца принята; не доказательство подтверждения реестром.",
        "required": [
          "domain_uuid",
          "external_id",
          "verification_uuid",
          "status"
        ],
        "properties": {
          "domain_uuid": {
            "type": "string",
            "format": "uuid",
            "description": "Домен."
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Внешний ID."
          },
          "verification_uuid": {
            "type": "string",
            "format": "uuid",
            "description": "Заявка."
          },
          "status": {
            "type": "string",
            "description": "Состояние заявки."
          }
        }
      },
      "WebhookAutoRenewFailureData": {
        "type": "object",
        "description": "Не удалось запустить автопродление. Одинаковый домен получает не более одного такого события за календарный час; подробности уточняйте по состоянию/поддержке.",
        "required": [
          "domain_uuid",
          "code",
          "message"
        ],
        "properties": {
          "domain_uuid": {
            "type": "string",
            "format": "uuid",
            "description": "Домен."
          },
          "code": {
            "type": "string",
            "const": "auto_renew_unavailable",
            "description": "Обобщённый код; не раскрывает конкретный баланс или внутреннюю ошибку."
          },
          "message": {
            "type": "string",
            "const": "Check reseller balance, renewal price, API key and domain restrictions.",
            "description": "Подсказка для диагностики."
          }
        }
      },
      "WebhookOrderData": {
        "type": "object",
        "description": "Снимок заказа. deleted присутствует у уведомлений жизненного цикла, но может отсутствовать в явном order.created.",
        "required": [
          "order_uuid",
          "external_id",
          "status"
        ],
        "properties": {
          "order_uuid": {
            "type": "string",
            "format": "uuid",
            "description": "Заказ."
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Внешний ID."
          },
          "status": {
            "type": "string",
            "description": "Статус заказа."
          },
          "deleted": {
            "type": "boolean",
            "description": "Признак локального удаления, если передан."
          }
        }
      },
      "WebhookServiceData": {
        "type": "object",
        "description": "Изменение услуги.",
        "required": [
          "service_uuid",
          "external_id",
          "status",
          "deleted",
          "auto_renew",
          "expires_at"
        ],
        "properties": {
          "service_uuid": {
            "type": "string",
            "format": "uuid",
            "description": "Услуга."
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Внешний ID."
          },
          "status": {
            "type": "string",
            "description": "Статус."
          },
          "deleted": {
            "type": "boolean",
            "description": "Признак локального удаления."
          },
          "auto_renew": {
            "type": "boolean",
            "description": "Автопродление услуги; не подменяет отдельные настройки reseller-автопродления домена."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Срок услуги."
          }
        }
      },
      "WebhookTicketCreatedData": {
        "type": "object",
        "description": "Создание обращения.",
        "required": [
          "ticket_uuid",
          "external_id",
          "status"
        ],
        "properties": {
          "ticket_uuid": {
            "type": "string",
            "format": "uuid",
            "description": "Обращение."
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Внешний ID."
          },
          "status": {
            "type": "string",
            "description": "Статус обращения."
          }
        }
      },
      "WebhookTicketUpdatedData": {
        "type": "object",
        "description": "Ответ через reseller API.",
        "required": [
          "ticket_uuid",
          "message_uuid",
          "status"
        ],
        "properties": {
          "ticket_uuid": {
            "type": "string",
            "format": "uuid",
            "description": "Обращение."
          },
          "message_uuid": {
            "type": "string",
            "format": "uuid",
            "description": "Сообщение."
          },
          "status": {
            "type": "string",
            "description": "Статус обращения после ответа."
          }
        }
      },
      "WebhookReservedData": {
        "type": "object",
        "additionalProperties": true,
        "description": "Форма не обещана: имя принимается в подписках, но действующего отправителя нет. Не генерируйте бизнес-логику или ожидание завершения на основе этого имени."
      },
      "WebhookEventEnvelope": {
        "type": "object",
        "description": "Исходящий POST application/json, подпись по исходным байтам. Нет event_version, request_id, delivery_uuid, correlation_id и порядкового номера в теле. provider может быть null. Разные попытки одного события сохраняют id; порядок не гарантирован.",
        "required": [
          "id",
          "type",
          "created_at",
          "environment",
          "provider",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "ID события; дедупликация по (environment,provider,id), при раздельной обработке подписок добавьте X-Webhook-ID."
          },
          "type": {
            "$ref": "#/components/schemas/WebhookEventType"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Время события, не попытки отправки; не версия ресурса."
          },
          "environment": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ],
            "description": "Среда."
          },
          "provider": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "UUID реселлера, null если связь недоступна."
          },
          "data": {
            "type": "object",
            "description": "Payload выбранного события; не полная копия ресурса."
          }
        },
        "allOf": [
          {
            "if": {
              "properties": {
                "type": {
                  "const": "customer.created"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookCustomerData"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "customer.updated"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/WebhookCustomerData"
                    },
                    {
                      "$ref": "#/components/schemas/WebhookContactData"
                    }
                  ]
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "domain.created"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookDomainState"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "domain.registration.updated"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookDomainState"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "domain.operation.updated"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookOperationData"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "domain.updated"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookDomainState"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "domain.nameservers.updated"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookReservedData"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "domain.contacts.updated"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookReservedData"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "domain.verification.submitted"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookVerificationData"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "domain.verification.updated"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookDomainState"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "domain.renewed"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookReservedData"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "domain.auto_renew.failed"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookAutoRenewFailureData"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "domain.transfer.requested"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookReservedData"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "domain.transfer.updated"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookDomainState"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "hosting.created"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookReservedData"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "hosting.updated"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookReservedData"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "order.created"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookOrderData"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "order.updated"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookOrderData"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "invoice.created"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookReservedData"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "invoice.updated"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookReservedData"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "payment.completed"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookReservedData"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "service.updated"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookServiceData"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "ticket.created"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookTicketCreatedData"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "ticket.updated"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookTicketUpdatedData"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "webhook.test"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/WebhookTestData"
                }
              }
            }
          }
        ],
        "examples": [
          {
            "id": "00000000-0000-4000-8000-000000000001",
            "type": "customer.created",
            "created_at": "2026-10-05T09:01:00+00:00",
            "environment": "test",
            "provider": "55555555-5555-4555-8555-555555555555",
            "data": {
              "customer_uuid": "88888888-8888-4888-8888-888888888888",
              "external_id": "customer-42"
            }
          },
          {
            "id": "00000000-0000-4000-8000-000000000002",
            "type": "customer.updated",
            "created_at": "2026-10-05T09:01:00+00:00",
            "environment": "test",
            "provider": "55555555-5555-4555-8555-555555555555",
            "data": {
              "customer_uuid": "88888888-8888-4888-8888-888888888888",
              "contact_uuid": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
              "action": "contact_updated"
            }
          },
          {
            "id": "00000000-0000-4000-8000-000000000003",
            "type": "domain.created",
            "created_at": "2026-10-05T09:01:00+00:00",
            "environment": "test",
            "provider": "55555555-5555-4555-8555-555555555555",
            "data": {
              "domain_uuid": "88888888-8888-4888-8888-888888888888",
              "external_id": "domain-42",
              "status": "provisioning",
              "deleted": false,
              "domain_name": "sample.kz",
              "registration_status": "queued",
              "verification_status": "pending",
              "edu_license_status": "not_required",
              "registry_status": null,
              "expires_at": null,
              "registrar_operation_status": null,
              "verification_registry_status": null,
              "edu_license_registry_status": null,
              "hold_status": null,
              "transfer": {
                "status": null,
                "registry_transfer_status": null
              }
            }
          },
          {
            "id": "00000000-0000-4000-8000-000000000004",
            "type": "domain.registration.updated",
            "created_at": "2026-10-05T09:01:00+00:00",
            "environment": "test",
            "provider": "55555555-5555-4555-8555-555555555555",
            "data": {
              "domain_uuid": "88888888-8888-4888-8888-888888888888",
              "external_id": "domain-42",
              "status": "active",
              "deleted": false,
              "domain_name": "sample.kz",
              "registration_status": "registered",
              "verification_status": "verified",
              "edu_license_status": "not_required",
              "registry_status": "ok",
              "expires_at": "2027-10-05T09:00:00+00:00",
              "registrar_operation_status": null,
              "verification_registry_status": null,
              "edu_license_registry_status": null,
              "hold_status": null,
              "transfer": {
                "status": null,
                "registry_transfer_status": null
              }
            }
          },
          {
            "id": "00000000-0000-4000-8000-000000000005",
            "type": "domain.operation.updated",
            "created_at": "2026-10-05T09:01:00+00:00",
            "environment": "test",
            "provider": "55555555-5555-4555-8555-555555555555",
            "data": {
              "operation_uuid": "66666666-6666-4666-8666-666666666666",
              "domain_uuid": "88888888-8888-4888-8888-888888888888",
              "action": "renew",
              "status": "completed"
            }
          },
          {
            "id": "00000000-0000-4000-8000-000000000006",
            "type": "domain.updated",
            "created_at": "2026-10-05T09:01:00+00:00",
            "environment": "test",
            "provider": "55555555-5555-4555-8555-555555555555",
            "data": {
              "domain_uuid": "88888888-8888-4888-8888-888888888888",
              "external_id": "domain-42",
              "status": "active",
              "deleted": false,
              "domain_name": "sample.kz",
              "registration_status": "registered",
              "verification_status": "verified",
              "edu_license_status": "not_required",
              "registry_status": "ok",
              "expires_at": "2027-10-05T09:00:00+00:00",
              "registrar_operation_status": null,
              "verification_registry_status": null,
              "edu_license_registry_status": null,
              "hold_status": null,
              "transfer": {
                "status": null,
                "registry_transfer_status": null
              }
            }
          },
          {
            "id": "00000000-0000-4000-8000-000000000007",
            "type": "domain.verification.submitted",
            "created_at": "2026-10-05T09:01:00+00:00",
            "environment": "test",
            "provider": "55555555-5555-4555-8555-555555555555",
            "data": {
              "domain_uuid": "88888888-8888-4888-8888-888888888888",
              "external_id": "domain-42",
              "verification_uuid": "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
              "status": "pending"
            }
          },
          {
            "id": "00000000-0000-4000-8000-000000000008",
            "type": "domain.verification.updated",
            "created_at": "2026-10-05T09:01:00+00:00",
            "environment": "test",
            "provider": "55555555-5555-4555-8555-555555555555",
            "data": {
              "domain_uuid": "88888888-8888-4888-8888-888888888888",
              "external_id": "domain-42",
              "status": "active",
              "deleted": false,
              "domain_name": "sample.kz",
              "registration_status": "registered",
              "verification_status": "verified",
              "edu_license_status": "not_required",
              "registry_status": "ok",
              "expires_at": "2027-10-05T09:00:00+00:00",
              "registrar_operation_status": null,
              "verification_registry_status": null,
              "edu_license_registry_status": null,
              "hold_status": null,
              "transfer": {
                "status": null,
                "registry_transfer_status": null
              }
            }
          },
          {
            "id": "00000000-0000-4000-8000-000000000009",
            "type": "domain.auto_renew.failed",
            "created_at": "2026-10-05T09:01:00+00:00",
            "environment": "test",
            "provider": "55555555-5555-4555-8555-555555555555",
            "data": {
              "domain_uuid": "88888888-8888-4888-8888-888888888888",
              "code": "auto_renew_unavailable",
              "message": "Check reseller balance, renewal price, API key and domain restrictions."
            }
          },
          {
            "id": "00000000-0000-4000-8000-000000000010",
            "type": "domain.transfer.updated",
            "created_at": "2026-10-05T09:01:00+00:00",
            "environment": "test",
            "provider": "55555555-5555-4555-8555-555555555555",
            "data": {
              "domain_uuid": "88888888-8888-4888-8888-888888888888",
              "external_id": "domain-42",
              "status": "active",
              "deleted": false,
              "domain_name": "sample.kz",
              "registration_status": "registered",
              "verification_status": "verified",
              "edu_license_status": "not_required",
              "registry_status": "ok",
              "expires_at": "2027-10-05T09:00:00+00:00",
              "registrar_operation_status": null,
              "verification_registry_status": null,
              "edu_license_registry_status": null,
              "hold_status": null,
              "transfer": {
                "status": "completed",
                "registry_transfer_status": "clientApproved"
              }
            }
          },
          {
            "id": "00000000-0000-4000-8000-000000000011",
            "type": "order.created",
            "created_at": "2026-10-05T09:01:00+00:00",
            "environment": "test",
            "provider": "55555555-5555-4555-8555-555555555555",
            "data": {
              "order_uuid": "88888888-8888-4888-8888-888888888888",
              "external_id": "order-42",
              "status": "completed",
              "deleted": false
            }
          },
          {
            "id": "00000000-0000-4000-8000-000000000012",
            "type": "order.updated",
            "created_at": "2026-10-05T09:01:00+00:00",
            "environment": "test",
            "provider": "55555555-5555-4555-8555-555555555555",
            "data": {
              "order_uuid": "88888888-8888-4888-8888-888888888888",
              "external_id": "order-42",
              "status": "completed",
              "deleted": false
            }
          },
          {
            "id": "00000000-0000-4000-8000-000000000013",
            "type": "service.updated",
            "created_at": "2026-10-05T09:01:00+00:00",
            "environment": "test",
            "provider": "55555555-5555-4555-8555-555555555555",
            "data": {
              "service_uuid": "88888888-8888-4888-8888-888888888888",
              "external_id": "service-42",
              "status": "active",
              "deleted": false,
              "auto_renew": false,
              "expires_at": "2027-10-05T09:00:00+00:00"
            }
          },
          {
            "id": "00000000-0000-4000-8000-000000000014",
            "type": "ticket.created",
            "created_at": "2026-10-05T09:01:00+00:00",
            "environment": "test",
            "provider": "55555555-5555-4555-8555-555555555555",
            "data": {
              "ticket_uuid": "88888888-8888-4888-8888-888888888888",
              "external_id": "ticket-42",
              "status": "open"
            }
          },
          {
            "id": "00000000-0000-4000-8000-000000000015",
            "type": "ticket.updated",
            "created_at": "2026-10-05T09:01:00+00:00",
            "environment": "test",
            "provider": "55555555-5555-4555-8555-555555555555",
            "data": {
              "ticket_uuid": "88888888-8888-4888-8888-888888888888",
              "message_uuid": "cccccccc-cccc-4ccc-8ccc-cccccccccccc",
              "status": "open"
            }
          },
          {
            "id": "00000000-0000-4000-8000-000000000016",
            "type": "webhook.test",
            "created_at": "2026-10-05T09:01:00+00:00",
            "environment": "test",
            "provider": "55555555-5555-4555-8555-555555555555",
            "data": {
              "webhook_uuid": "22222222-2222-4222-8222-222222222222"
            }
          }
        ]
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "Обязателен для POST/PUT/PATCH/DELETE, включая check, sync, test, rotate-secret. После trim 1..160 символов. Один ключ на логическую операцию и API-ключ. Повторите те же method/path/query и байты JSON. TTL HTTP replay по умолчанию 24 часа от первого резервирования; не продлевается. Не переиспользуйте ключ для новой операции, доменная задача дополнительно сохраняет связь с ним.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 160
        },
        "example": "webhook-create-20261005-0001"
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "description": "Непрозрачное значение meta.next_cursor. На первой странице не передаётся; не конструируйте вручную.",
        "schema": {
          "type": "string"
        }
      },
      "PerPage": {
        "name": "per_page",
        "in": "query",
        "description": "Рекомендуется 1..100, по умолчанию 25. Реализация приводит к integer и ограничивает 1..100, не возвращает 422 за выход из диапазона.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 25
        },
        "example": 25
      },
      "RequestId": {
        "name": "X-Request-ID",
        "in": "header",
        "description": "Необязательный UUID корреляции попытки; не заменяет Idempotency-Key.",
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "example": "11111111-1111-4111-8111-111111111111"
      },
      "WebhookUuid": {
        "name": "webhookUuid",
        "in": "path",
        "required": true,
        "description": "UUID из ответа API. Передавайте корректную строку UUID. Ресурс другого реселлера или среды не раскрывается.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "DomainUuid": {
        "name": "domainUuid",
        "in": "path",
        "required": true,
        "description": "UUID из ответа API. Передавайте корректную строку UUID. Ресурс другого реселлера или среды не раскрывается.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "CustomerUuid": {
        "name": "customerUuid",
        "in": "path",
        "required": true,
        "description": "UUID из ответа API. Передавайте корректную строку UUID. Ресурс другого реселлера или среды не раскрывается.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "OrderUuid": {
        "name": "orderUuid",
        "in": "path",
        "required": true,
        "description": "UUID из ответа API. Передавайте корректную строку UUID. Ресурс другого реселлера или среды не раскрывается.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "ServiceUuid": {
        "name": "serviceUuid",
        "in": "path",
        "required": true,
        "description": "UUID из ответа API. Передавайте корректную строку UUID. Ресурс другого реселлера или среды не раскрывается.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "ContactUuid": {
        "name": "contactUuid",
        "in": "path",
        "required": true,
        "description": "UUID из ответа API. Передавайте корректную строку UUID. Ресурс другого реселлера или среды не раскрывается.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "TicketUuid": {
        "name": "ticketUuid",
        "in": "path",
        "required": true,
        "description": "UUID из ответа API. Передавайте корректную строку UUID. Ресурс другого реселлера или среды не раскрывается.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "OperationUuid": {
        "name": "operationUuid",
        "in": "path",
        "required": true,
        "description": "UUID из ответа API. Передавайте корректную строку UUID. Ресурс другого реселлера или среды не раскрывается.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "DeliveryUuid": {
        "name": "deliveryUuid",
        "in": "path",
        "required": true,
        "description": "UUID из ответа API. Передавайте корректную строку UUID. Ресурс другого реселлера или среды не раскрывается.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      }
    },
    "headers": {
      "RequestId": {
        "description": "UUID текущего запроса. Корректный входной X-Request-ID принимается в нижнем регистре, иначе создаётся новый. Не гарантирован для proxy/неизвестного маршрута.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "RateLimitLimit": {
        "description": "Лимит API-ключа на 60 секунд. После прохождения ограничителя, включая replay. Отсутствует в собственной ветке 429 и при раннем отказе.",
        "schema": {
          "type": "integer",
          "minimum": 1
        },
        "example": 120
      },
      "RateLimitRemaining": {
        "description": "Остаток после запроса. Снимок, не гарантия при конкуренции. Отсутствует в собственной ветке 429 и при раннем отказе.",
        "schema": {
          "type": "integer",
          "minimum": 0
        },
        "example": 119
      },
      "RetryAfter": {
        "description": "На 429 API: целые секунды до сброса окна, не дата/Unix timestamp. При 0 добавьте jitter. Proxy может вернуть HTTP-date.",
        "schema": {
          "type": "string",
          "pattern": "^[0-9]+$"
        },
        "example": "42"
      },
      "IdempotencyReplayed": {
        "description": "Только при возврате сохранённого HTTP-ответа: true, не false. Отсутствие не доказывает, что доменная операция ранее не выполнялась.",
        "schema": {
          "type": "string",
          "const": "true"
        }
      },
      "WebhookId": {
        "description": "UUID подписки. Заголовок исходящего POST к реселлеру.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "WebhookTimestamp": {
        "description": "Unix seconds начала попытки; включён в HMAC. Проверяйте свежесть на своей стороне.",
        "schema": {
          "type": "string",
          "pattern": "^[0-9]+$"
        }
      },
      "WebhookSignature": {
        "description": "v1= плюс lowercase hex HMAC-SHA256(secret, timestamp + '.' + raw_body). Нет kid или второго секрета.",
        "schema": {
          "type": "string",
          "pattern": "^v1=[a-f0-9]{64}$"
        }
      },
      "WebhookRequestId": {
        "description": "UUID доставки для корреляции, стабилен при автоматических попытках и redeliver; отличается от HTTP-запроса к API, создавшего событие.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      }
    },
    "responses": {
      "Error400": {
        "description": "Неверный запрос. Парсер или proxy могут вернуть иной формат.",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                {
                  "$ref": "#/components/schemas/FrameworkError"
                }
              ]
            },
            "examples": {
              "default": {
                "value": {
                  "error": {
                    "code": "request_failed",
                    "message": "Bad Request",
                    "details": [],
                    "request_id": "11111111-1111-4111-8111-111111111111"
                  }
                }
              }
            }
          }
        }
      },
      "Error401": {
        "description": "Отсутствует, неверен, отозван или просрочен Bearer-ключ.",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "examples": {
              "default": {
                "value": {
                  "error": {
                    "code": "unauthenticated",
                    "message": "API credential is missing or invalid.",
                    "details": [],
                    "request_id": "11111111-1111-4111-8111-111111111111"
                  }
                }
              }
            }
          }
        }
      },
      "Error403": {
        "description": "Нет scope, IP не разрешён, тариф/режим/статус реселлера запрещает действие либо test-ключ обращается к live-only ресурсу.",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "examples": {
              "default": {
                "value": {
                  "error": {
                    "code": "forbidden",
                    "message": "Source IP address is not allowed.",
                    "details": [],
                    "request_id": "11111111-1111-4111-8111-111111111111"
                  }
                }
              },
              "supportTest": {
                "value": {
                  "error": {
                    "code": "forbidden",
                    "message": "Support is available only to live credentials.",
                    "details": [],
                    "request_id": "11111111-1111-4111-8111-111111111111"
                  }
                }
              }
            }
          }
        }
      },
      "Error404": {
        "description": "Ресурс недоступен в контексте или весь reseller API отключён. Не отличайте чужое от отсутствующего.",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "examples": {
              "default": {
                "value": {
                  "error": {
                    "code": "resource_not_found",
                    "message": "Not Found",
                    "details": [],
                    "request_id": "11111111-1111-4111-8111-111111111111"
                  }
                }
              }
            }
          }
        }
      },
      "Error405": {
        "description": "Метод не поддерживается. Обычно отклоняется до reseller-конверта.",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                {
                  "$ref": "#/components/schemas/FrameworkError"
                }
              ]
            },
            "examples": {
              "default": {
                "value": {
                  "message": "The PUT method is not supported for this route. Supported methods: GET, HEAD, POST."
                }
              }
            }
          }
        }
      },
      "Error409": {
        "description": "Конфликт ключа идемпотентности или состояния. Устраните причину.",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "examples": {
              "default": {
                "value": {
                  "error": {
                    "code": "conflict",
                    "message": "A request with this Idempotency-Key is already processing.",
                    "details": [],
                    "request_id": "11111111-1111-4111-8111-111111111111"
                  }
                }
              },
              "changedRequest": {
                "value": {
                  "error": {
                    "code": "conflict",
                    "message": "Idempotency-Key was already used with a different request.",
                    "details": [],
                    "request_id": "11111111-1111-4111-8111-111111111111"
                  }
                }
              }
            }
          }
        }
      },
      "Error422": {
        "description": "Ошибка данных или обязательного заголовка. details может быть [].",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "examples": {
              "default": {
                "value": {
                  "error": {
                    "code": "validation_failed",
                    "message": "A valid Idempotency-Key header is required.",
                    "details": [],
                    "request_id": "11111111-1111-4111-8111-111111111111"
                  }
                }
              },
              "fields": {
                "value": {
                  "error": {
                    "code": "validation_failed",
                    "message": "The name field is required.",
                    "details": {
                      "name": [
                        "The name field is required."
                      ]
                    },
                    "request_id": "11111111-1111-4111-8111-111111111111"
                  }
                }
              }
            }
          }
        }
      },
      "Error429": {
        "description": "Квота API-ключа исчерпана. Ждите Retry-After. Собственный 429 не содержит X-RateLimit-Limit/Remaining/Reset.",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/RequestId"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "examples": {
              "default": {
                "value": {
                  "error": {
                    "code": "rate_limit_exceeded",
                    "message": "API rate limit exceeded.",
                    "details": {
                      "retry_after": 42
                    },
                    "request_id": "11111111-1111-4111-8111-111111111111"
                  }
                }
              }
            }
          }
        }
      },
      "Error500": {
        "description": "Этот HTTP-ответ не определяет результат записи. Сначала сверка; повтор только с прежним ключом и точным телом.",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "examples": {
              "default": {
                "value": {
                  "error": {
                    "code": "internal_error",
                    "message": "The request could not be completed.",
                    "details": [],
                    "request_id": "11111111-1111-4111-8111-111111111111"
                  }
                }
              }
            }
          }
        }
      }
    },
    "securitySchemes": {
      "resellerBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "rsl_{test|live}_{publicKeyId}.{secret}",
        "description": "API-ключ реселлера, не токен кабинета. publicKeyId: 24 alphanumeric; secret: 64 base64url. Среда определяется ключом. Scopes описаны для каждой операции; OAuth2 не используется."
      }
    },
    "examples": {
      "DomainRegistered": {
        "summary": "Полная локальная запись зарегистрированного домена",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000001",
            "external_id": "crm-domain-1001",
            "service_uuid": "019a1234-1000-7000-8000-000000000005",
            "customer_uuid": "019a1234-1000-7000-8000-000000000002",
            "domain_name": "example.kz",
            "unicode_domain_name": "example.kz",
            "punycode_domain_name": "example.kz",
            "status": "active",
            "registration_status": "registered",
            "registrar_operation_status": "success",
            "registry_status": "ok",
            "verification_status": "pending",
            "verification_registry_status": null,
            "edu_license_status": null,
            "hold_status": "none",
            "hold_reason": null,
            "registered_at": "2026-10-01T08:00:00+00:00",
            "expires_at": "2027-10-01T08:00:00+00:00",
            "last_synced_at": "2026-10-05T08:00:00+00:00",
            "zone": {
              "uuid": "019a1234-1000-7000-8000-000000000004",
              "zone": "kz",
              "display_name": ".kz"
            },
            "contacts": {
              "owner": {
                "uuid": "019a1234-1000-7000-8000-000000000003",
                "type": "person",
                "status": "active",
                "first_name": "Ivan",
                "last_name": "Petrov",
                "middle_name": null,
                "organization_name": null,
                "email": "owner@example.net",
                "phone": "+77000000000",
                "country_code": "KZ",
                "residence_country_code": "KZ",
                "city": "Almaty",
                "region": "Almaty",
                "address_line": "Example street 1",
                "postal_code": "050000",
                "external_id_type": "IIN",
                "verification_status": "pending",
                "is_locked_as_registrant": false,
                "verified_at": null
              },
              "admin": {
                "uuid": "019a1234-1000-7000-8000-000000000003",
                "type": "person",
                "status": "active",
                "first_name": "Ivan",
                "last_name": "Petrov",
                "middle_name": null,
                "organization_name": null,
                "email": "owner@example.net",
                "phone": "+77000000000",
                "country_code": "KZ",
                "residence_country_code": "KZ",
                "city": "Almaty",
                "region": "Almaty",
                "address_line": "Example street 1",
                "postal_code": "050000",
                "external_id_type": "IIN",
                "verification_status": "pending",
                "is_locked_as_registrant": false,
                "verified_at": null
              },
              "tech": {
                "uuid": "019a1234-1000-7000-8000-000000000003",
                "type": "person",
                "status": "active",
                "first_name": "Ivan",
                "last_name": "Petrov",
                "middle_name": null,
                "organization_name": null,
                "email": "owner@example.net",
                "phone": "+77000000000",
                "country_code": "KZ",
                "residence_country_code": "KZ",
                "city": "Almaty",
                "region": "Almaty",
                "address_line": "Example street 1",
                "postal_code": "050000",
                "external_id_type": "IIN",
                "verification_status": "pending",
                "is_locked_as_registrant": false,
                "verified_at": null
              },
              "billing": {
                "uuid": "019a1234-1000-7000-8000-000000000003",
                "type": "person",
                "status": "active",
                "first_name": "Ivan",
                "last_name": "Petrov",
                "middle_name": null,
                "organization_name": null,
                "email": "owner@example.net",
                "phone": "+77000000000",
                "country_code": "KZ",
                "residence_country_code": "KZ",
                "city": "Almaty",
                "region": "Almaty",
                "address_line": "Example street 1",
                "postal_code": "050000",
                "external_id_type": "IIN",
                "verification_status": "pending",
                "is_locked_as_registrant": false,
                "verified_at": null
              }
            },
            "nameservers": [
              {
                "hostname": "ns1.example.net",
                "ipv4": null,
                "ipv6": null
              },
              {
                "hostname": "ns2.example.net",
                "ipv4": null,
                "ipv6": null
              }
            ],
            "created_at": "2026-10-01T07:59:00+00:00",
            "updated_at": "2026-10-05T08:00:00+00:00"
          }
        }
      },
      "DomainList": {
        "summary": "Последняя страница с одним доменом",
        "value": {
          "data": [
            {
              "uuid": "019a1234-1000-7000-8000-000000000001",
              "external_id": "crm-domain-1001",
              "service_uuid": "019a1234-1000-7000-8000-000000000005",
              "customer_uuid": "019a1234-1000-7000-8000-000000000002",
              "domain_name": "example.kz",
              "unicode_domain_name": "example.kz",
              "punycode_domain_name": "example.kz",
              "status": "active",
              "registration_status": "registered",
              "registrar_operation_status": "success",
              "registry_status": "ok",
              "verification_status": "pending",
              "verification_registry_status": null,
              "edu_license_status": null,
              "hold_status": "none",
              "hold_reason": null,
              "registered_at": "2026-10-01T08:00:00+00:00",
              "expires_at": "2027-10-01T08:00:00+00:00",
              "last_synced_at": "2026-10-05T08:00:00+00:00",
              "zone": {
                "uuid": "019a1234-1000-7000-8000-000000000004",
                "zone": "kz",
                "display_name": ".kz"
              },
              "contacts": {
                "owner": {
                  "uuid": "019a1234-1000-7000-8000-000000000003",
                  "type": "person",
                  "status": "active",
                  "first_name": "Ivan",
                  "last_name": "Petrov",
                  "middle_name": null,
                  "organization_name": null,
                  "email": "owner@example.net",
                  "phone": "+77000000000",
                  "country_code": "KZ",
                  "residence_country_code": "KZ",
                  "city": "Almaty",
                  "region": "Almaty",
                  "address_line": "Example street 1",
                  "postal_code": "050000",
                  "external_id_type": "IIN",
                  "verification_status": "pending",
                  "is_locked_as_registrant": false,
                  "verified_at": null
                },
                "admin": {
                  "uuid": "019a1234-1000-7000-8000-000000000003",
                  "type": "person",
                  "status": "active",
                  "first_name": "Ivan",
                  "last_name": "Petrov",
                  "middle_name": null,
                  "organization_name": null,
                  "email": "owner@example.net",
                  "phone": "+77000000000",
                  "country_code": "KZ",
                  "residence_country_code": "KZ",
                  "city": "Almaty",
                  "region": "Almaty",
                  "address_line": "Example street 1",
                  "postal_code": "050000",
                  "external_id_type": "IIN",
                  "verification_status": "pending",
                  "is_locked_as_registrant": false,
                  "verified_at": null
                },
                "tech": {
                  "uuid": "019a1234-1000-7000-8000-000000000003",
                  "type": "person",
                  "status": "active",
                  "first_name": "Ivan",
                  "last_name": "Petrov",
                  "middle_name": null,
                  "organization_name": null,
                  "email": "owner@example.net",
                  "phone": "+77000000000",
                  "country_code": "KZ",
                  "residence_country_code": "KZ",
                  "city": "Almaty",
                  "region": "Almaty",
                  "address_line": "Example street 1",
                  "postal_code": "050000",
                  "external_id_type": "IIN",
                  "verification_status": "pending",
                  "is_locked_as_registrant": false,
                  "verified_at": null
                },
                "billing": {
                  "uuid": "019a1234-1000-7000-8000-000000000003",
                  "type": "person",
                  "status": "active",
                  "first_name": "Ivan",
                  "last_name": "Petrov",
                  "middle_name": null,
                  "organization_name": null,
                  "email": "owner@example.net",
                  "phone": "+77000000000",
                  "country_code": "KZ",
                  "residence_country_code": "KZ",
                  "city": "Almaty",
                  "region": "Almaty",
                  "address_line": "Example street 1",
                  "postal_code": "050000",
                  "external_id_type": "IIN",
                  "verification_status": "pending",
                  "is_locked_as_registrant": false,
                  "verified_at": null
                }
              },
              "nameservers": [
                {
                  "hostname": "ns1.example.net",
                  "ipv4": null,
                  "ipv6": null
                },
                {
                  "hostname": "ns2.example.net",
                  "ipv4": null,
                  "ipv6": null
                }
              ],
              "created_at": "2026-10-01T07:59:00+00:00",
              "updated_at": "2026-10-05T08:00:00+00:00"
            }
          ],
          "links": {
            "first": null,
            "last": null,
            "prev": null,
            "next": null
          },
          "meta": {
            "path": "https://api.example.net/api/reseller/v1/domains",
            "per_page": 25,
            "next_cursor": null,
            "prev_cursor": null
          }
        }
      },
      "DomainEmptyList": {
        "summary": "Нет подходящих доменов",
        "value": {
          "data": [],
          "links": {
            "first": null,
            "last": null,
            "prev": null,
            "next": null
          },
          "meta": {
            "path": "https://api.example.net/api/reseller/v1/domains",
            "per_page": 25,
            "next_cursor": null,
            "prev_cursor": null
          }
        }
      },
      "DomainOperationPending": {
        "summary": "Принято, EPP ещё не подтверждён",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "renew",
            "status": "pending",
            "result": [],
            "error": null,
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": null,
            "next_check_at": "2026-10-05T08:00:00+00:00"
          }
        }
      },
      "DomainOperationProcessing": {
        "summary": "Обработчик выполняет запрос",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "renew",
            "status": "processing",
            "result": [],
            "error": null,
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": null,
            "next_check_at": null
          }
        }
      },
      "DomainOperationCompleted": {
        "summary": "Подтверждённое продление",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "renew",
            "status": "completed",
            "result": {
              "domain_uuid": "019a1234-1000-7000-8000-000000000001",
              "expires_at": "2028-10-01T08:00:00+00:00"
            },
            "error": null,
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": "2026-10-05T08:00:03+00:00",
            "next_check_at": null
          }
        }
      },
      "DomainOperationFailed": {
        "summary": "Окончательный отказ; проверьте ограничения, резерв освобождён",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "renew",
            "status": "failed",
            "result": [],
            "error": {
              "code": "registry_rejected",
              "message": "Operation was rejected. Check domain data and registrar restrictions."
            },
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": null,
            "next_check_at": null
          }
        }
      },
      "DomainOperationUncertain": {
        "summary": "Результат неизвестен; новую команду отправлять нельзя",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "renew",
            "status": "uncertain",
            "result": [],
            "error": {
              "code": "registry_result_unknown",
              "message": "Registry result is not confirmed. Do not repeat this operation; reconciliation is scheduled."
            },
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": null,
            "next_check_at": "2026-10-05T08:05:00+00:00"
          }
        }
      },
      "DomainOperationAuthCode": {
        "summary": "GET с дополнительным domains.auth-code; код демонстрационный",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "auth_code",
            "status": "completed",
            "result": {
              "domain_uuid": "019a1234-1000-7000-8000-000000000001",
              "expires_at": "2027-10-01T08:00:00+00:00",
              "auth_code": "DEMO-NOT-A-REAL-AUTH-CODE"
            },
            "error": null,
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": "2026-10-05T08:00:03+00:00",
            "next_check_at": null
          }
        }
      },
      "DomainOperationAuthCodeRedacted": {
        "summary": "GET без domains.auth-code: поле auth_code отсутствует",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "auth_code",
            "status": "completed",
            "result": {
              "domain_uuid": "019a1234-1000-7000-8000-000000000001",
              "expires_at": "2027-10-01T08:00:00+00:00"
            },
            "error": null,
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": "2026-10-05T08:00:03+00:00",
            "next_check_at": null
          }
        }
      },
      "DomainOperationList": {
        "summary": "История доменных операций",
        "value": {
          "data": [
            {
              "uuid": "019a1234-1000-7000-8000-000000000007",
              "domain_uuid": "019a1234-1000-7000-8000-000000000001",
              "action": "renew",
              "status": "completed",
              "result": {
                "domain_uuid": "019a1234-1000-7000-8000-000000000001",
                "expires_at": "2028-10-01T08:00:00+00:00"
              },
              "error": null,
              "created_at": "2026-10-05T08:00:00+00:00",
              "completed_at": "2026-10-05T08:00:03+00:00",
              "next_check_at": null
            },
            {
              "uuid": "019a1234-1000-7000-8000-000000000012",
              "domain_uuid": "019a1234-1000-7000-8000-000000000001",
              "action": "renew",
              "status": "failed",
              "result": [],
              "error": {
                "code": "registry_rejected",
                "message": "Operation was rejected. Check domain data and registrar restrictions."
              },
              "created_at": "2026-10-05T08:00:00+00:00",
              "completed_at": null,
              "next_check_at": null
            }
          ],
          "meta": {
            "next_cursor": null
          }
        }
      },
      "DomainAvailable": {
        "summary": "Имя доступно на момент проверки; пример не гарантирует реальную доступность",
        "value": {
          "data": {
            "domain": "example.kz",
            "available": true,
            "reason": null,
            "registrar": "nic_kz"
          }
        }
      },
      "DomainUnavailable": {
        "summary": "Имя занято",
        "value": {
          "data": {
            "domain": "example.kz",
            "available": false,
            "reason": "Object exists",
            "registrar": "nic_kz"
          }
        }
      },
      "DomainCapabilities": {
        "summary": "Пример возможностей настроенного драйвера; проверяйте ответ конкретного домена",
        "value": {
          "data": {
            "operations": {
              "renew": true,
              "transfer": true,
              "transfer_query": true,
              "transfer_approve": true,
              "transfer_reject": true,
              "transfer_cancel": true,
              "restore": true,
              "nameservers": true,
              "contacts": true,
              "statuses": true,
              "delete": true,
              "auth_code": true,
              "privacy": true,
              "sync": true,
              "host_create": true,
              "host_update": true,
              "host_delete": true,
              "host_info": true,
              "dnssec": false
            },
            "renewal_available_at": "2026-10-02T08:00:00+00:00",
            "maximum_term_years": 10,
            "auto_renew": false,
            "whois_privacy": {
              "enabled": false,
              "status": "disabled",
              "hidden_fields": [],
              "available_fields": [
                {
                  "code": "name",
                  "label": "Имя",
                  "registrar_field": "contact:name",
                  "description": "Настройка раскрытия поля Имя"
                },
                {
                  "code": "organization",
                  "label": "Организация",
                  "registrar_field": "contact:org",
                  "description": "Настройка раскрытия поля Организация"
                },
                {
                  "code": "address",
                  "label": "Адрес",
                  "registrar_field": "contact:addr",
                  "description": "Настройка раскрытия поля Адрес"
                },
                {
                  "code": "phone",
                  "label": "Телефон",
                  "registrar_field": "contact:voice",
                  "description": "Настройка раскрытия поля Телефон"
                },
                {
                  "code": "fax",
                  "label": "Факс",
                  "registrar_field": "contact:fax",
                  "description": "Настройка раскрытия поля Факс"
                },
                {
                  "code": "email",
                  "label": "Email",
                  "registrar_field": "contact:email",
                  "description": "Настройка раскрытия поля Email"
                }
              ],
              "last_applied_at": null
            }
          }
        }
      },
      "DomainVerificationPayload": {
        "summary": "Полный документ и SHA-256; данные демонстрационные, не подписывать этот пример",
        "value": {
          "data": {
            "type": "eds",
            "eds_provider": "ncalayer",
            "doc_spec_alg": "DOC-SPEC-REGISTRANT-IDENTITY-CONFIRMATION-V1",
            "signer_role": "CURRENT_REGISTRANT",
            "payload_hash": "4f7ec7ab0a228267838add37968eb6f14cce085679ece865136db71afe11fb51",
            "signable_payload": "[KK] ТІРКЕУШІНІҢ ТІРКЕУ ДЕРЕКТЕРІН РАСТАУ ТУРАЛЫ ӨТІНІШ\n\nОсы өтініш арқылы мен көрсетілген домендік атаудың тіркеу деректерінің өзектілігі мен дұрыстығын Қазақстан интернет\nсегментіндегі домендік атауларды тіркеу, пайдалану және бөлу қағидаларына сәйкес толық растаймын.\n\nОПЕРАЦИЯ ТУРАЛЫ ДЕРЕКТЕР:\n---------------------------------------------------------------------------\n* Домендік атау: example.kz\n* Құрылған күні: 2026-10-01T08:00:00.000Z\n* Тіркеуші (Т.А.Ә.): Ivan Petrov\n* Ұйым: \n* Ел: KZ\n* Құжат түрі: IIN\n* Құжат нөмірі: 000000000000\n\nБұл құжат Қазақстандық торап ақпарат орталығына (KazNIC) жіберуге арналған.\n\nKazNIC. Барлық құқықтар қорғалған.\n\n\n[RU] ЗАЯВЛЕНИЕ НА ПОДТВЕРЖДЕНИЕ РЕГИСТРАЦИОННЫХ ДАННЫХ\n\nНастоящим действием я в полной мере подтверждаю актуальность и достоверность регистрационных данных указанного\nдоменного имени в соответствии с Правилами регистрации, пользования и распределения доменных имен в пространстве\nказахстанского сегмента Интернета.\n\nДАННЫЕ ОПЕРАЦИИ:\n---------------------------------------------------------------------------\n* Доменное имя: example.kz\n* Дата создания: 2026-10-01T08:00:00.000Z\n* Регистрант (ФИО): Ivan Petrov\n* Организация: \n* Страна: KZ\n* Тип документа: IIN\n* Номер документа: 000000000000\n\nДанный документ предназначен для отправки в Казахстанский центр сетевой информации (KazNIC).\n\nKazNIC. Все права защищены.",
            "payload_snapshot": {
              "domain-name": "example.kz",
              "domain-creation-time": "2026-10-01T08:00:00.000Z",
              "registrant-name": "Ivan Petrov",
              "registrant-org": null,
              "registrant-residencedetails-country": "KZ",
              "registrant-residencedetails-externalidtype": "IIN",
              "registrant-residencedetails-externalidvalue": "000000000000"
            },
            "issued_at": "2026-10-05T08:00:00+00:00",
            "expires_at": "2026-10-05T08:15:00+00:00",
            "ncalayer_options": {
              "environment": "production",
              "allowedStorages": null,
              "locale": "ru",
              "signerParams": {
                "extKeyUsageOids": [
                  "1.3.6.1.5.5.7.3.4"
                ],
                "chain": null
              }
            },
            "signature_options": {
              "method": "kz.gov.pki.knca.basics.sign",
              "format": "cms",
              "decode": false,
              "encapsulate": false,
              "digested": false,
              "timestamp_applied": true,
              "cms_type": "CMS Detached",
              "cades_profile": "CAdES-T"
            }
          }
        }
      },
      "DomainVerificationSubmission": {
        "summary": "Структура отправки; замените signature результатом подписи документа из GET",
        "value": {
          "type": "eds",
          "payload_snapshot": {
            "domain-name": "example.kz",
            "domain-creation-time": "2026-10-01T08:00:00.000Z",
            "registrant-name": "Ivan Petrov",
            "registrant-org": null,
            "registrant-residencedetails-country": "KZ",
            "registrant-residencedetails-externalidtype": "IIN",
            "registrant-residencedetails-externalidvalue": "000000000000"
          },
          "signable_payload": "[KK] ТІРКЕУШІНІҢ ТІРКЕУ ДЕРЕКТЕРІН РАСТАУ ТУРАЛЫ ӨТІНІШ\n\nОсы өтініш арқылы мен көрсетілген домендік атаудың тіркеу деректерінің өзектілігі мен дұрыстығын Қазақстан интернет\nсегментіндегі домендік атауларды тіркеу, пайдалану және бөлу қағидаларына сәйкес толық растаймын.\n\nОПЕРАЦИЯ ТУРАЛЫ ДЕРЕКТЕР:\n---------------------------------------------------------------------------\n* Домендік атау: example.kz\n* Құрылған күні: 2026-10-01T08:00:00.000Z\n* Тіркеуші (Т.А.Ә.): Ivan Petrov\n* Ұйым: \n* Ел: KZ\n* Құжат түрі: IIN\n* Құжат нөмірі: 000000000000\n\nБұл құжат Қазақстандық торап ақпарат орталығына (KazNIC) жіберуге арналған.\n\nKazNIC. Барлық құқықтар қорғалған.\n\n\n[RU] ЗАЯВЛЕНИЕ НА ПОДТВЕРЖДЕНИЕ РЕГИСТРАЦИОННЫХ ДАННЫХ\n\nНастоящим действием я в полной мере подтверждаю актуальность и достоверность регистрационных данных указанного\nдоменного имени в соответствии с Правилами регистрации, пользования и распределения доменных имен в пространстве\nказахстанского сегмента Интернета.\n\nДАННЫЕ ОПЕРАЦИИ:\n---------------------------------------------------------------------------\n* Доменное имя: example.kz\n* Дата создания: 2026-10-01T08:00:00.000Z\n* Регистрант (ФИО): Ivan Petrov\n* Организация: \n* Страна: KZ\n* Тип документа: IIN\n* Номер документа: 000000000000\n\nДанный документ предназначен для отправки в Казахстанский центр сетевой информации (KazNIC).\n\nKazNIC. Все права защищены.",
          "signature": "REPLACE_WITH_BASE64_DER_CADES_T_FROM_NCALAYER",
          "certificate": null,
          "eds_provider": "ncalayer",
          "signature_options": {
            "method": "kz.gov.pki.knca.basics.sign",
            "format": "cms",
            "decode": false,
            "encapsulate": false,
            "digested": false,
            "timestamp_applied": true,
            "cms_type": "CMS Detached",
            "cades_profile": "CAdES-T"
          }
        }
      },
      "DomainVerificationPending": {
        "summary": "Подпись принята, ожидается реестр",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000010",
            "type": "eds",
            "status": "pending",
            "eds_provider": "ncalayer",
            "verification_status": "pending",
            "verified_at": null,
            "failed_at": null,
            "expires_at": null,
            "failure_message": null
          }
        }
      },
      "DomainVerificationVerified": {
        "summary": "Владелец подтверждён",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000010",
            "type": "eds",
            "status": "verified",
            "eds_provider": "ncalayer",
            "verification_status": "verified",
            "verified_at": "2026-10-05T08:00:05+00:00",
            "failed_at": null,
            "expires_at": null,
            "failure_message": null
          }
        }
      },
      "DomainVerificationFailed": {
        "summary": "HTTP 201: попытка создана, но подпись отклонена",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000010",
            "type": "eds",
            "status": "failed",
            "eds_provider": "ncalayer",
            "verification_status": "failed",
            "verified_at": null,
            "failed_at": "2026-10-05T08:00:05+00:00",
            "expires_at": null,
            "failure_message": "Подписанный документ не совпадает с актуальными данными владельца."
          }
        }
      },
      "DomainRegistrationOrder": {
        "summary": "Заказ при внешнем checkout; сумма только пример, цену берите из quote",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000008",
            "external_id": "crm-order-1001",
            "customer_uuid": "019a1234-1000-7000-8000-000000000002",
            "number": "ORD-2026-1001",
            "status": "paid",
            "currency": "KZT",
            "total_minor": 500000,
            "submitted_at": "2026-10-05T08:00:00+00:00",
            "paid_at": "2026-10-05T08:00:00+00:00",
            "completed_at": null,
            "failed_at": null,
            "failure_reason": null,
            "items": [
              {
                "uuid": "019a1234-1000-7000-8000-000000000011",
                "type": "domain",
                "name": "example.kz",
                "quantity": 1,
                "unit_price_minor": 500000,
                "total_minor": 500000,
                "currency": "KZT",
                "service_uuid": "019a1234-1000-7000-8000-000000000005",
                "service_external_id": "crm-domain-1001"
              }
            ],
            "invoice": {
              "uuid": "019a1234-1000-7000-8000-000000000009",
              "number": "INV-2026-1001",
              "status": "paid",
              "currency": "KZT",
              "total_minor": 500000,
              "paid_minor": 500000,
              "payment_collected_by_platform": false
            },
            "created_at": "2026-10-05T08:00:00+00:00",
            "updated_at": "2026-10-05T08:00:00+00:00"
          }
        }
      },
      "DomainRegistrationAwaitingPayment": {
        "summary": "Заказ с оплатой через платформу; регистрация ещё не выполнена",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000008",
            "external_id": "crm-order-1001",
            "customer_uuid": "019a1234-1000-7000-8000-000000000002",
            "number": "ORD-2026-1001",
            "status": "submitted",
            "currency": "KZT",
            "total_minor": 500000,
            "submitted_at": "2026-10-05T08:00:00+00:00",
            "paid_at": null,
            "completed_at": null,
            "failed_at": null,
            "failure_reason": null,
            "items": [
              {
                "uuid": "019a1234-1000-7000-8000-000000000011",
                "type": "domain",
                "name": "example.kz",
                "quantity": 1,
                "unit_price_minor": 500000,
                "total_minor": 500000,
                "currency": "KZT",
                "service_uuid": "019a1234-1000-7000-8000-000000000005",
                "service_external_id": "crm-domain-1001"
              }
            ],
            "invoice": {
              "uuid": "019a1234-1000-7000-8000-000000000009",
              "number": "INV-2026-1001",
              "status": "issued",
              "currency": "KZT",
              "total_minor": 500000,
              "paid_minor": 0,
              "payment_collected_by_platform": true
            },
            "created_at": "2026-10-05T08:00:00+00:00",
            "updated_at": "2026-10-05T08:00:00+00:00"
          }
        }
      },
      "DomainAcceptedRenewDomain": {
        "summary": "Принята операция renew",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "renew",
            "status": "pending",
            "result": [],
            "error": null,
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": null,
            "next_check_at": "2026-10-05T08:00:00+00:00"
          }
        }
      },
      "DomainAcceptedTransferDomain": {
        "summary": "Принята операция transfer",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "transfer",
            "status": "pending",
            "result": [],
            "error": null,
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": null,
            "next_check_at": "2026-10-05T08:00:00+00:00"
          }
        }
      },
      "DomainAcceptedRestoreDomain": {
        "summary": "Принята операция restore",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "restore",
            "status": "pending",
            "result": [],
            "error": null,
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": null,
            "next_check_at": "2026-10-05T08:00:00+00:00"
          }
        }
      },
      "DomainAcceptedUpdateDomainNameservers": {
        "summary": "Принята операция nameservers",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "nameservers",
            "status": "pending",
            "result": [],
            "error": null,
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": null,
            "next_check_at": "2026-10-05T08:00:00+00:00"
          }
        }
      },
      "DomainAcceptedUpdateDomainContacts": {
        "summary": "Принята операция contacts",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "contacts",
            "status": "pending",
            "result": [],
            "error": null,
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": null,
            "next_check_at": "2026-10-05T08:00:00+00:00"
          }
        }
      },
      "DomainAcceptedUpdateDomainStatuses": {
        "summary": "Принята операция statuses",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "statuses",
            "status": "pending",
            "result": [],
            "error": null,
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": null,
            "next_check_at": "2026-10-05T08:00:00+00:00"
          }
        }
      },
      "DomainAcceptedSetDomainWhoisPrivacy": {
        "summary": "Принята операция privacy",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "privacy",
            "status": "pending",
            "result": [],
            "error": null,
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": null,
            "next_check_at": "2026-10-05T08:00:00+00:00"
          }
        }
      },
      "DomainAcceptedSynchronizeDomain": {
        "summary": "Принята операция sync",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "sync",
            "status": "pending",
            "result": [],
            "error": null,
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": null,
            "next_check_at": "2026-10-05T08:00:00+00:00"
          }
        }
      },
      "DomainAcceptedRefreshDomainAuthCode": {
        "summary": "Принята операция auth_code",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "auth_code",
            "status": "pending",
            "result": [],
            "error": null,
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": null,
            "next_check_at": "2026-10-05T08:00:00+00:00"
          }
        }
      },
      "DomainAcceptedQueryDomainTransfer": {
        "summary": "Принята операция transfer_query",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "transfer_query",
            "status": "pending",
            "result": [],
            "error": null,
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": null,
            "next_check_at": "2026-10-05T08:00:00+00:00"
          }
        }
      },
      "DomainAcceptedApproveDomainTransfer": {
        "summary": "Принята операция transfer_approve",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "transfer_approve",
            "status": "pending",
            "result": [],
            "error": null,
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": null,
            "next_check_at": "2026-10-05T08:00:00+00:00"
          }
        }
      },
      "DomainAcceptedRejectDomainTransfer": {
        "summary": "Принята операция transfer_reject",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "transfer_reject",
            "status": "pending",
            "result": [],
            "error": null,
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": null,
            "next_check_at": "2026-10-05T08:00:00+00:00"
          }
        }
      },
      "DomainAcceptedCancelDomainTransfer": {
        "summary": "Принята операция transfer_cancel",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "transfer_cancel",
            "status": "pending",
            "result": [],
            "error": null,
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": null,
            "next_check_at": "2026-10-05T08:00:00+00:00"
          }
        }
      },
      "DomainAcceptedCreateDomainHost": {
        "summary": "Принята операция host_create",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "host_create",
            "status": "pending",
            "result": [],
            "error": null,
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": null,
            "next_check_at": "2026-10-05T08:00:00+00:00"
          }
        }
      },
      "DomainAcceptedUpdateDomainHost": {
        "summary": "Принята операция host_update",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "host_update",
            "status": "pending",
            "result": [],
            "error": null,
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": null,
            "next_check_at": "2026-10-05T08:00:00+00:00"
          }
        }
      },
      "DomainAcceptedDeleteDomainHost": {
        "summary": "Принята операция host_delete",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "host_delete",
            "status": "pending",
            "result": [],
            "error": null,
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": null,
            "next_check_at": "2026-10-05T08:00:00+00:00"
          }
        }
      },
      "DomainAcceptedDeleteDomain": {
        "summary": "Принята операция delete",
        "value": {
          "data": {
            "uuid": "019a1234-1000-7000-8000-000000000007",
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "action": "delete",
            "status": "pending",
            "result": [],
            "error": null,
            "created_at": "2026-10-05T08:00:00+00:00",
            "completed_at": null,
            "next_check_at": "2026-10-05T08:00:00+00:00"
          }
        }
      },
      "DomainHostExists": {
        "summary": "Объект host существует",
        "value": {
          "data": {
            "hostname": "ns1.example.kz",
            "exists": true,
            "status": "active",
            "addresses": [
              "192.0.2.10",
              "2001:db8::10"
            ],
            "statuses": [
              "ok"
            ],
            "createdAt": "2026-10-01T08:00:00Z",
            "updatedAt": null
          }
        }
      },
      "DomainHostAbsent": {
        "summary": "Host не существует; это не HTTP 404 домена",
        "value": {
          "data": {
            "hostname": "ns1.example.kz",
            "exists": false,
            "status": "not_found",
            "addresses": [],
            "statuses": [],
            "createdAt": null,
            "updatedAt": null
          }
        }
      },
      "DomainAutoRenewEnabled": {
        "summary": "Согласие сохранено",
        "value": {
          "data": {
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "auto_renew": true,
            "period_years": 1,
            "days_before_expiry": 7
          }
        }
      },
      "DomainAutoRenewDisabled": {
        "summary": "Будущие автопродления выключены",
        "value": {
          "data": {
            "domain_uuid": "019a1234-1000-7000-8000-000000000001",
            "auto_renew": false,
            "period_years": 1,
            "days_before_expiry": 7
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "Контекст"
    },
    {
      "name": "Каталог"
    },
    {
      "name": "Котировки"
    },
    {
      "name": "Заказы"
    },
    {
      "name": "Хостинг"
    },
    {
      "name": "Услуги"
    },
    {
      "name": "Покупатели"
    },
    {
      "name": "Контакты"
    },
    {
      "name": "Финансы"
    },
    {
      "name": "Поддержка"
    },
    {
      "name": "Domains"
    },
    {
      "name": "Operations"
    },
    {
      "name": "Webhooks"
    }
  ],
  "security": [
    {
      "resellerBearer": []
    }
  ]
}
