Тема
Получить точный документ для ЭЦП владельца
GET /domains/{domainUuid}/registrant-verification/payload
operationId: getRegistrantVerificationPayload
Требуемые scopes: domains.verify.
Домен должен быть создан в реестре; нужны активная интеграция и отсутствие 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.
Лимит общий для всех запросов одного API-ключа (rate_limit_per_minute, значение по умолчанию при выдаче ключа 120, индивидуальное значение может отличаться). Окно счётчика 60 секунд. Учитывайте X-RateLimit-Limit и X-RateLimit-Remaining; при 429 дождитесь Retry-After секунд и добавьте jitter. Polling и повторы также расходуют лимит; лимиты реестра независимы.
Scopes: domains.verify
x-input-caveats:
json
[
"Передавайте синтаксически корректный UUID. Не все методы выполняют явную UUID-валидацию до SQL; поведение malformed UUID не унифицировано и в PostgreSQL может дать 500 вместо 404/422. Это ограничение реализации, не рекомендуемый сценарий клиента."
]Параметры
domainUuid
Расположение: path. Обязательный. UUID из ответа API. Передавайте корректную строку UUID. Ресурс другого реселлера или среды не раскрывается.
| Поле | Тип | Обязательно в родителе | Ограничения | Описание |
|---|---|---|---|---|
$ | string | Да | format: "uuid" |
Тело запроса
Тело запроса отсутствует.
Пример запроса
bash
API_BASE_URL='https://api.b.websoft.kz/api/reseller/v1'
: "${RESELLER_API_TOKEN:?Set RESELLER_API_TOKEN in your environment}"
: "${DOMAIN_UUID:?Set DOMAIN_UUID}"
curl --request GET "$API_BASE_URL/domains/${DOMAIN_UUID}/registrant-verification/payload" \
--connect-timeout 5 --max-time 20 --fail-with-body \
--header "Authorization: Bearer $RESELLER_API_TOKEN" \
--header 'Accept: application/json'php
<?php
declare(strict_types=1);
// PHP 8.0+; extensions: curl, json. Persist the key/body before any write.
function requiredEnv(string $name): string
{
$value = getenv($name);
if ($value === false || $value === '' || str_contains($value, "\r") || str_contains($value, "\n")) {
throw new RuntimeException('Set a valid environment variable: ' . $name);
}
return $value;
}
$baseUrl = rtrim(getenv('API_BASE_URL') ?: 'https://api.b.websoft.kz/api/reseller/v1', '/');
$path = '/domains/{domainUuid}/registrant-verification/payload';
$path = str_replace('{domainUuid}', rawurlencode(requiredEnv('DOMAIN_UUID')), $path);
$url = $baseUrl . $path;
$headers = [
'Authorization: Bearer ' . requiredEnv('RESELLER_API_TOKEN'),
'Accept: application/json',
];
$responseHeaders = [];
$handle = curl_init($url);
if ($handle === false) {
throw new RuntimeException('Cannot initialize cURL.');
}
try {
if (!curl_setopt_array($handle, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_TIMEOUT => 20,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_HEADERFUNCTION => static function (CurlHandle $curl, string $line) use (&$responseHeaders): int {
if (str_starts_with($line, 'HTTP/')) {
$responseHeaders = [];
} elseif (str_contains($line, ':')) {
[$name, $value] = explode(':', $line, 2);
$responseHeaders[strtolower(trim($name))] = trim($value);
}
return strlen($line);
},
])) {
throw new RuntimeException('Cannot configure cURL.');
}
$raw = curl_exec($handle);
if ($raw === false) {
throw new RuntimeException('Transport failure; result may be unknown. cURL code: ' . curl_errno($handle));
}
$status = (int) curl_getinfo($handle, CURLINFO_RESPONSE_CODE);
} finally {
unset($handle);
}
$requestId = $responseHeaders['x-request-id'] ?? null;
$retryAfter = $responseHeaders['retry-after'] ?? null;
$data = null;
if ($raw !== '' && !in_array($status, [204, 205], true)) {
try {
$data = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $error) {
if ($status >= 200 && $status < 300) {
throw new RuntimeException('Unexpected non-JSON success response.', 0, $error);
}
}
}
if ($status < 200 || $status >= 300) {
// Inspect $data['error']; queue 429 using $retryAfter, never create a new write key blindly.
throw new RuntimeException('HTTP ' . $status . '; request_id=' . ($requestId ?? '-') . '; retry_after=' . ($retryAfter ?? '-'));
}
// $data contains the decoded response; 202 means accepted, not completed.
echo 'HTTP ' . $status . PHP_EOL;js
// Node.js 22+; save as request.mjs. No third-party dependencies.
function requiredEnv(name) {
const value = process.env[name];
if (!value || /[\r\n]/u.test(value)) throw new Error('Set a valid environment variable: ' + name);
return value;
}
const baseUrl = (process.env.API_BASE_URL || "https://api.b.websoft.kz/api/reseller/v1").replace(/\/$/u, '');
let path = "/domains/{domainUuid}/registrant-verification/payload";
path = path.replaceAll("{domainUuid}", encodeURIComponent(requiredEnv("DOMAIN_UUID")));
const url = baseUrl + path;
const response = await fetch(url, {
method: "GET",
redirect: 'manual',
signal: AbortSignal.timeout(20_000),
headers: {
Authorization: 'Bearer ' + requiredEnv('RESELLER_API_TOKEN'),
Accept: 'application/json',
},
});
const requestId = response.headers.get('x-request-id');
const retryAfter = response.headers.get('retry-after');
const raw = await response.text();
let data = null;
if (raw && ![204, 205].includes(response.status)) {
try { data = JSON.parse(raw); }
catch (cause) { if (response.ok) throw new Error('Unexpected non-JSON success response.', { cause }); }
}
if (!response.ok) {
// Inspect data?.error; schedule 429 using retryAfter. Do not generate another write key.
throw new Error('HTTP ' + response.status + '; request_id=' + requestId + '; retry_after=' + retryAfter);
}
// A timeout may have an unknown result. No automatic mutation retry in this example.
// data contains the response; 202 means accepted, not completed.
console.log('HTTP', response.status);python
# Python 3.10+; standard library only.
import json
import os
from urllib.error import HTTPError
from urllib.parse import quote
from urllib.request import HTTPRedirectHandler, Request, build_opener
def required_env(name):
value = os.environ.get(name, "")
if not value or "\r" in value or "\n" in value:
raise RuntimeError("Set a valid environment variable: " + name)
return value
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
base_url = os.environ.get("API_BASE_URL", "https://api.b.websoft.kz/api/reseller/v1").rstrip("/")
path = "/domains/{domainUuid}/registrant-verification/payload"
path = path.replace("{domainUuid}", quote(required_env("DOMAIN_UUID"), safe=""))
url = base_url + path
headers = {
"Authorization": "Bearer " + required_env("RESELLER_API_TOKEN"),
"Accept": "application/json",
}
body = None
request = Request(url, data=body, headers=headers, method="GET")
opener = build_opener(NoRedirect())
try:
response = opener.open(request, timeout=20)
except HTTPError as error:
response = error # HTTP failure still has a response body and headers.
# Network/timeout exceptions propagate: a write may already have been accepted.
with response:
status = response.status
request_id = response.headers.get("X-Request-ID")
retry_after = response.headers.get("Retry-After")
raw = response.read()
data = None
if raw and status not in (204, 205):
try:
data = json.loads(raw)
except (ValueError, UnicodeDecodeError):
if 200 <= status < 300:
raise RuntimeError("Unexpected non-JSON success response.")
if not 200 <= status < 300:
# Inspect data['error']; schedule 429 with retry_after, keeping the same persisted key.
raise RuntimeError(f"HTTP {status}; request_id={request_id}; retry_after={retry_after}")
# data contains the response. Do not log secrets/PII; 202 is not completion.
print("HTTP", status)go
// Go 1.22+; standard library only. Save as main.go and run: go run main.go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"time"
)
func requiredEnv(name string) string {
value := os.Getenv(name)
if value == "" || strings.ContainsAny(value, "\r\n") { panic("Set a valid environment variable: " + name) }
return value
}
func main() {
baseURL := os.Getenv("API_BASE_URL")
if baseURL == "" { baseURL = "https://api.b.websoft.kz/api/reseller/v1" }
path := "/domains/{domainUuid}/registrant-verification/payload"
value0 := requiredEnv("DOMAIN_UUID")
path = strings.ReplaceAll(path, "{domainUuid}", url.PathEscape(value0))
endpoint := strings.TrimRight(baseURL, "/") + path
request, err := http.NewRequest("GET", endpoint, nil)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer " + requiredEnv("RESELLER_API_TOKEN"))
request.Header.Set("Accept", "application/json")
client := &http.Client{
Timeout: 20 * time.Second,
CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse },
}
response, err := client.Do(request)
if err != nil { panic("Transport failure; operation result may be unknown") }
defer response.Body.Close()
raw, err := io.ReadAll(response.Body)
if err != nil { panic("Response read failed; operation result may be unknown") }
requestID := response.Header.Get("X-Request-ID")
retryAfter := response.Header.Get("Retry-After")
var data any
if len(raw) > 0 && response.StatusCode != 204 && response.StatusCode != 205 {
decoder := json.NewDecoder(strings.NewReader(string(raw)))
decoder.UseNumber() // Preserve integer money/identifiers without float rounding.
if err := decoder.Decode(&data); err != nil && response.StatusCode >= 200 && response.StatusCode < 300 { panic("Unexpected non-JSON success response") }
}
if response.StatusCode < 200 || response.StatusCode >= 300 {
// Inspect data; schedule 429 using retryAfter. Never create a new key blindly.
panic(fmt.Sprintf("HTTP %d; request_id=%s; retry_after=%s", response.StatusCode, requestID, retryAfter))
}
// data holds the JSON value; 202 means accepted, not completed. Do not log secrets.
fmt.Println("HTTP", response.StatusCode)
}java
// Java 17+; standard library only. Save as RequestExample.java.
// Run: java RequestExample.java
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
public class RequestExample {
private static String requiredEnv(String name) {
String value = System.getenv(name);
if (value == null || value.isEmpty() || value.contains("\r") || value.contains("\n")) {
throw new IllegalStateException("Set a valid environment variable: " + name);
}
return value;
}
private static String envOr(String name, String fallback) {
String value = System.getenv(name);
return value == null || value.isEmpty() ? fallback : value;
}
public static void main(String[] args) throws Exception {
String baseUrl = envOr("API_BASE_URL", "https://api.b.websoft.kz/api/reseller/v1").replaceAll("/+$", "");
String path = "/domains/{domainUuid}/registrant-verification/payload";
path = path.replace("{domainUuid}", URLEncoder.encode(requiredEnv("DOMAIN_UUID"), StandardCharsets.UTF_8).replace("+", "%20"));
URI uri = URI.create(baseUrl + path);
HttpRequest request = HttpRequest.newBuilder(uri)
.timeout(Duration.ofSeconds(20))
.header("Authorization", "Bearer " + requiredEnv("RESELLER_API_TOKEN"))
.header("Accept", "application/json")
.method("GET", HttpRequest.BodyPublishers.noBody())
.build();
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.followRedirects(HttpClient.Redirect.NEVER)
.build();
// Reuse HttpClient in production. A timeout may mean an unknown write result.
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
int status = response.statusCode();
String requestId = response.headers().firstValue("X-Request-ID").orElse("");
String retryAfter = response.headers().firstValue("Retry-After").orElse("");
String responseBody = (status == 204 || status == 205) ? "" : response.body();
if (status < 200 || status >= 300) {
// Decode responseBody with your JSON library; schedule 429 using retryAfter.
throw new IllegalStateException("HTTP " + status + "; request_id=" + requestId + "; retry_after=" + retryAfter);
}
// Java SE has no JSON object mapper: pass responseBody to your project's JSON parser.
// Do not parse an empty 204 body; 202 means accepted, not completed.
System.out.println("HTTP " + status);
}
}csharp
// C# / .NET 8+ console app; standard library only. Save as Program.cs.
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
static string RequiredEnv(string name)
{
string? value = Environment.GetEnvironmentVariable(name);
if (string.IsNullOrEmpty(value) || value.Contains('\r') || value.Contains('\n'))
throw new InvalidOperationException("Set a valid environment variable: " + name);
return value;
}
string baseUrl = (Environment.GetEnvironmentVariable("API_BASE_URL") ?? "https://api.b.websoft.kz/api/reseller/v1").TrimEnd('/');
string path = "/domains/{domainUuid}/registrant-verification/payload";
path = path.Replace("{domainUuid}", Uri.EscapeDataString(RequiredEnv("DOMAIN_UUID")));
using var handler = new HttpClientHandler { AllowAutoRedirect = false };
using var client = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(20) };
// Reuse HttpClient (or IHttpClientFactory) in production, not one client per request.
using var request = new HttpRequestMessage(new HttpMethod("GET"), baseUrl + path);
request.Headers.Add("Authorization", "Bearer " + RequiredEnv("RESELLER_API_TOKEN"));
request.Headers.Add("Accept", "application/json");
// Transport exceptions may have an unknown result; do not issue a new mutation automatically.
using var response = await client.SendAsync(request);
int status = (int)response.StatusCode;
string requestId = response.Headers.TryGetValues("X-Request-ID", out var ids) ? string.Join(",", ids) : "";
string retryAfter = response.Headers.TryGetValues("Retry-After", out var delays) ? string.Join(",", delays) : "";
string raw = await response.Content.ReadAsStringAsync();
JsonElement? data = null;
if (raw.Length > 0 && status != 204 && status != 205)
{
try { using var document = JsonDocument.Parse(raw); data = document.RootElement.Clone(); }
catch (JsonException) { if (response.IsSuccessStatusCode) throw; }
}
if (!response.IsSuccessStatusCode)
{
// Inspect data; schedule 429 using retryAfter and preserve the same key/body.
throw new HttpRequestException($"HTTP {status}; request_id={requestId}; retry_after={retryAfter}");
}
// data contains JSON, or null for 204. HTTP 202 is not completion.
Console.WriteLine($"HTTP {status}");Ответы
Условия ошибок и восстановление
| HTTP | Код | Когда возникает | Что делать |
|---|---|---|---|
| 401 | unauthenticated | Нет действительного Bearer API-ключа. | Проверьте ключ, срок действия и окружение; не повторяйте бесконечно. |
| 403 | forbidden | Нет требуемого scope, провайдер/ключ/доступ запрещён. | Проверьте настройки провайдера и scopes ключа. |
| 429 | rate_limit_exceeded | Исчерпан общий лимит ключа. | Выждите Retry-After; повторы выполняйте с backoff и jitter. |
| 500 | internal_error | Необработанный сбой инфраструктуры или внешнего драйвера. | Сохраните request_id. Для изменения сначала проверьте существующую задачу; не генерируйте новый ключ вслепую. Формат ответа reverse proxy может отличаться от API. |
| 404 | resource_not_found | UUID не найден у этого провайдера/окружения, либо запись удалена/скрыта migration hold. | Проверьте UUID, test/live и принадлежность. Не пытайтесь получить ресурс чужого провайдера. |
| 409 | conflict | Интеграция неактивна или migration hold. | Обратитесь к оператору. |
| 422 | validation_failed | Домен не создан в реестре, последняя проверка pending или уже verified. | Дождитесь завершения регистрации/верификации; повторная подпись не нужна. |
| 404 | resource_not_found | Reseller API глобально выключен конфигурацией. | Уточните base URL и включение API у оператора. |
HTTP 200
Успешное чтение или сохранение настройки.
Content-Type: application/json
| Поле | Тип | Обязательно в родителе | Ограничения | Описание |
|---|---|---|---|---|
$ | object | Да | required: ["data"] | Схема: DomainVerificationPayloadEnvelope |
$.data | object | Да | required: ["type","eds_provider","doc_spec_alg","signer_role","payload_hash","signable_payload","payload_snapshot","issued_at","expires_at","ncalayer_options","signature_options"] | |
$.data.type | string | Да | const: "eds" | Поддерживаемый способ. |
$.data.eds_provider | string | Да | const: "ncalayer" | Провайдер подписи. |
$.data.doc_spec_alg | string | Да | const: "DOC-SPEC-REGISTRANT-IDENTITY-CONFIRMATION-V1" | Версия подписываемого документа. |
$.data.signer_role | string | Да | const: "CURRENT_REGISTRANT" | Текущий владелец домена. |
$.data.payload_hash | string | Да | pattern: "^[a-f0-9]{64}$" | SHA-256 точной строки signable_payload. |
$.data.signable_payload | string | Да | Полный двуязычный документ. Подписывайте без изменения пробелов, переносов, кодировки UTF-8 и порядка строк. | |
$.data.payload_snapshot | object | Да | required: ["domain-name","domain-creation-time","registrant-name","registrant-org","registrant-residencedetails-country","registrant-residencedetails-externalidtype","registrant-residencedetails-externalidvalue"] | Схема: DomainVerificationSnapshot |
$.data.payload_snapshot.domain-name | string | Да | Точное имя из реестра, Punycode. | |
$.data.payload_snapshot.domain-creation-time | string / null | Да | Дата создания в EPP, сохраняйте исходную строку. | |
$.data.payload_snapshot.registrant-name | string / null | Да | Имя владельца из реестра/манифеста. | |
$.data.payload_snapshot.registrant-org | string / null | Да | Организация владельца либо null. | |
$.data.payload_snapshot.registrant-residencedetails-country | string / null | Да | Страна резидентства. | |
$.data.payload_snapshot.registrant-residencedetails-externalidtype | string / null | Да | Тип документа, например IIN/BIN. | |
$.data.payload_snapshot.registrant-residencedetails-externalidvalue | string / null | Да | Чувствительный идентификатор владельца; не логируйте. | |
$.data.issued_at | string | Да | format: "date-time" | Время формирования. |
$.data.expires_at | string | Да | format: "date-time" | Рекомендуемое время обновления документа (issued_at + 15 минут). При submit сервер сверяет актуальные данные и точный текст, но отдельный timestamp/token срока не принимает. |
$.data.ncalayer_options | object | Да | required: ["environment","allowedStorages","locale","signerParams"] | |
$.data.ncalayer_options.environment | string | Да | Окружение NCALayer, test или production. | |
$.data.ncalayer_options.allowedStorages | anyOf | Да | Разрешённые хранилища или null в production. | |
$.data.ncalayer_options.allowedStorages (anyOf 1) | array | Да | В test [PKCS12]. | |
$.data.ncalayer_options.allowedStorages (anyOf 1)[] | string | Да | Идентификатор хранилища. | |
$.data.ncalayer_options.allowedStorages (anyOf 2) | null | Да | ||
$.data.ncalayer_options.locale | string | Да | Локаль NCALayer. | |
$.data.ncalayer_options.signerParams | object | Да | required: ["extKeyUsageOids","chain"] | |
$.data.ncalayer_options.signerParams.extKeyUsageOids | array | Да | Ограничения назначения сертификата. | |
$.data.ncalayer_options.signerParams.extKeyUsageOids[] | string | Да | OID назначения ключа. | |
$.data.ncalayer_options.signerParams.chain | anyOf | Да | Цепочка сертификатов или null. | |
$.data.ncalayer_options.signerParams.chain (anyOf 1) | array | Да | Тестовая цепочка. | |
$.data.ncalayer_options.signerParams.chain (anyOf 1)[] | string | Да | Сертификат цепочки. | |
$.data.ncalayer_options.signerParams.chain (anyOf 2) | null | Да | ||
$.data.signature_options | object | Да | required: ["method","format","decode","encapsulate","digested","timestamp_applied","cms_type","cades_profile"] | Схема: DomainSignatureOptions |
$.data.signature_options.method | string | Да | maxLength: 80 | Для действительной подписи строго kz.gov.pki.knca.basics.sign. |
$.data.signature_options.format | string | Да | maxLength: 20 | Для действительной подписи строго cms. |
$.data.signature_options.decode | boolean | Да | const: false | Подписываются исходные текстовые байты, не Base64-декодированный документ. |
$.data.signature_options.encapsulate | boolean | Да | const: false | Только detached CMS. |
$.data.signature_options.digested | boolean | Да | const: false | Не передавать предварительный дайджест вместо документа. |
$.data.signature_options.timestamp_applied | boolean | Да | const: true | Нужна метка времени CAdES-T. |
$.data.signature_options.cms_type | string | Да | maxLength: 80 | Для действительной подписи строго CMS Detached. |
$.data.signature_options.cades_profile | string | Да | maxLength: 80 | Для действительной подписи строго CAdES-T. |
Полный документ и SHA-256; данные демонстрационные, не подписывать этот пример (200)
json
{
"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"
}
}
}HTTP 401
Отсутствует, неверен, отозван или просрочен Bearer-ключ.
Заголовок X-Request-ID: UUID текущего запроса. Корректный входной X-Request-ID принимается в нижнем регистре, иначе создаётся новый. Не гарантирован для proxy/неизвестного маршрута.
| Поле | Тип | Обязательно в родителе | Ограничения | Описание |
|---|---|---|---|---|
$ | string | Да | format: "uuid" |
Content-Type: application/json
| Поле | Тип | Обязательно в родителе | Ограничения | Описание |
|---|---|---|---|---|
$ | object | Да | required: ["error"] | Собственный JSON-конверт API, не RFC 9457. Структура и code не зависят от языка; message и details предназначены человеку. До выбора маршрута и на proxy этот конверт не гарантирован. Схема: ErrorEnvelope |
$.error | object | Да | required: ["code","message","details","request_id"] | |
$.error.code | string | Да | Машинный код; не извлекайте причину из message. | |
$.error.message | string | Да | Человекочитаемое, частично локализуемое сообщение; не стабильный идентификатор ошибки. | |
$.error.details | oneOf | Да | Без подробностей возвращается [], не null и не {}. Валидация: {поле:[сообщения]}. Квота: {retry_after:секунды}. | |
$.error.details (oneOf 1) | array | Да | maxItems: 0 | |
$.error.details (oneOf 2) | object | Да | ||
$.error.details (oneOf 2).* | array | Нет | ||
$.error.details (oneOf 2).*[] | string | Да | ||
$.error.details (oneOf 3) | object | Да | required: ["retry_after"]; Дополнительные поля запрещены | |
$.error.details (oneOf 3).retry_after | integer | Да | minimum: 0 | |
$.error.request_id | string / null | Да | format: "uuid" | UUID для диагностики или null без контекста. При HTTP replay тело может содержать исходный ID, заголовок X-Request-ID относится к текущей попытке. |
default (401)
json
{
"error": {
"code": "unauthenticated",
"message": "API credential is missing or invalid.",
"details": [],
"request_id": "11111111-1111-4111-8111-111111111111"
}
}HTTP 403
Нет scope, IP не разрешён, тариф/режим/статус реселлера запрещает действие либо test-ключ обращается к live-only ресурсу.
Заголовок X-Request-ID: UUID текущего запроса. Корректный входной X-Request-ID принимается в нижнем регистре, иначе создаётся новый. Не гарантирован для proxy/неизвестного маршрута.
| Поле | Тип | Обязательно в родителе | Ограничения | Описание |
|---|---|---|---|---|
$ | string | Да | format: "uuid" |
Content-Type: application/json
| Поле | Тип | Обязательно в родителе | Ограничения | Описание |
|---|---|---|---|---|
$ | object | Да | required: ["error"] | Собственный JSON-конверт API, не RFC 9457. Структура и code не зависят от языка; message и details предназначены человеку. До выбора маршрута и на proxy этот конверт не гарантирован. Схема: ErrorEnvelope |
$.error | object | Да | required: ["code","message","details","request_id"] | |
$.error.code | string | Да | Машинный код; не извлекайте причину из message. | |
$.error.message | string | Да | Человекочитаемое, частично локализуемое сообщение; не стабильный идентификатор ошибки. | |
$.error.details | oneOf | Да | Без подробностей возвращается [], не null и не {}. Валидация: {поле:[сообщения]}. Квота: {retry_after:секунды}. | |
$.error.details (oneOf 1) | array | Да | maxItems: 0 | |
$.error.details (oneOf 2) | object | Да | ||
$.error.details (oneOf 2).* | array | Нет | ||
$.error.details (oneOf 2).*[] | string | Да | ||
$.error.details (oneOf 3) | object | Да | required: ["retry_after"]; Дополнительные поля запрещены | |
$.error.details (oneOf 3).retry_after | integer | Да | minimum: 0 | |
$.error.request_id | string / null | Да | format: "uuid" | UUID для диагностики или null без контекста. При HTTP replay тело может содержать исходный ID, заголовок X-Request-ID относится к текущей попытке. |
default (403)
json
{
"error": {
"code": "forbidden",
"message": "Source IP address is not allowed.",
"details": [],
"request_id": "11111111-1111-4111-8111-111111111111"
}
}supportTest (403)
json
{
"error": {
"code": "forbidden",
"message": "Support is available only to live credentials.",
"details": [],
"request_id": "11111111-1111-4111-8111-111111111111"
}
}HTTP 404
Ресурс недоступен в контексте или весь reseller API отключён. Не отличайте чужое от отсутствующего.
Заголовок X-Request-ID: UUID текущего запроса. Корректный входной X-Request-ID принимается в нижнем регистре, иначе создаётся новый. Не гарантирован для proxy/неизвестного маршрута.
| Поле | Тип | Обязательно в родителе | Ограничения | Описание |
|---|---|---|---|---|
$ | string | Да | format: "uuid" |
Content-Type: application/json
| Поле | Тип | Обязательно в родителе | Ограничения | Описание |
|---|---|---|---|---|
$ | object | Да | required: ["error"] | Собственный JSON-конверт API, не RFC 9457. Структура и code не зависят от языка; message и details предназначены человеку. До выбора маршрута и на proxy этот конверт не гарантирован. Схема: ErrorEnvelope |
$.error | object | Да | required: ["code","message","details","request_id"] | |
$.error.code | string | Да | Машинный код; не извлекайте причину из message. | |
$.error.message | string | Да | Человекочитаемое, частично локализуемое сообщение; не стабильный идентификатор ошибки. | |
$.error.details | oneOf | Да | Без подробностей возвращается [], не null и не {}. Валидация: {поле:[сообщения]}. Квота: {retry_after:секунды}. | |
$.error.details (oneOf 1) | array | Да | maxItems: 0 | |
$.error.details (oneOf 2) | object | Да | ||
$.error.details (oneOf 2).* | array | Нет | ||
$.error.details (oneOf 2).*[] | string | Да | ||
$.error.details (oneOf 3) | object | Да | required: ["retry_after"]; Дополнительные поля запрещены | |
$.error.details (oneOf 3).retry_after | integer | Да | minimum: 0 | |
$.error.request_id | string / null | Да | format: "uuid" | UUID для диагностики или null без контекста. При HTTP replay тело может содержать исходный ID, заголовок X-Request-ID относится к текущей попытке. |
default (404)
json
{
"error": {
"code": "resource_not_found",
"message": "Not Found",
"details": [],
"request_id": "11111111-1111-4111-8111-111111111111"
}
}HTTP 409
Конфликт ключа идемпотентности или состояния. Устраните причину.
Заголовок X-Request-ID: UUID текущего запроса. Корректный входной X-Request-ID принимается в нижнем регистре, иначе создаётся новый. Не гарантирован для proxy/неизвестного маршрута.
| Поле | Тип | Обязательно в родителе | Ограничения | Описание |
|---|---|---|---|---|
$ | string | Да | format: "uuid" |
Content-Type: application/json
| Поле | Тип | Обязательно в родителе | Ограничения | Описание |
|---|---|---|---|---|
$ | object | Да | required: ["error"] | Собственный JSON-конверт API, не RFC 9457. Структура и code не зависят от языка; message и details предназначены человеку. До выбора маршрута и на proxy этот конверт не гарантирован. Схема: ErrorEnvelope |
$.error | object | Да | required: ["code","message","details","request_id"] | |
$.error.code | string | Да | Машинный код; не извлекайте причину из message. | |
$.error.message | string | Да | Человекочитаемое, частично локализуемое сообщение; не стабильный идентификатор ошибки. | |
$.error.details | oneOf | Да | Без подробностей возвращается [], не null и не {}. Валидация: {поле:[сообщения]}. Квота: {retry_after:секунды}. | |
$.error.details (oneOf 1) | array | Да | maxItems: 0 | |
$.error.details (oneOf 2) | object | Да | ||
$.error.details (oneOf 2).* | array | Нет | ||
$.error.details (oneOf 2).*[] | string | Да | ||
$.error.details (oneOf 3) | object | Да | required: ["retry_after"]; Дополнительные поля запрещены | |
$.error.details (oneOf 3).retry_after | integer | Да | minimum: 0 | |
$.error.request_id | string / null | Да | format: "uuid" | UUID для диагностики или null без контекста. При HTTP replay тело может содержать исходный ID, заголовок X-Request-ID относится к текущей попытке. |
default (409)
json
{
"error": {
"code": "conflict",
"message": "A request with this Idempotency-Key is already processing.",
"details": [],
"request_id": "11111111-1111-4111-8111-111111111111"
}
}changedRequest (409)
json
{
"error": {
"code": "conflict",
"message": "Idempotency-Key was already used with a different request.",
"details": [],
"request_id": "11111111-1111-4111-8111-111111111111"
}
}HTTP 422
Ошибка данных или обязательного заголовка. details может быть [].
Заголовок X-Request-ID: UUID текущего запроса. Корректный входной X-Request-ID принимается в нижнем регистре, иначе создаётся новый. Не гарантирован для proxy/неизвестного маршрута.
| Поле | Тип | Обязательно в родителе | Ограничения | Описание |
|---|---|---|---|---|
$ | string | Да | format: "uuid" |
Content-Type: application/json
| Поле | Тип | Обязательно в родителе | Ограничения | Описание |
|---|---|---|---|---|
$ | object | Да | required: ["error"] | Собственный JSON-конверт API, не RFC 9457. Структура и code не зависят от языка; message и details предназначены человеку. До выбора маршрута и на proxy этот конверт не гарантирован. Схема: ErrorEnvelope |
$.error | object | Да | required: ["code","message","details","request_id"] | |
$.error.code | string | Да | Машинный код; не извлекайте причину из message. | |
$.error.message | string | Да | Человекочитаемое, частично локализуемое сообщение; не стабильный идентификатор ошибки. | |
$.error.details | oneOf | Да | Без подробностей возвращается [], не null и не {}. Валидация: {поле:[сообщения]}. Квота: {retry_after:секунды}. | |
$.error.details (oneOf 1) | array | Да | maxItems: 0 | |
$.error.details (oneOf 2) | object | Да | ||
$.error.details (oneOf 2).* | array | Нет | ||
$.error.details (oneOf 2).*[] | string | Да | ||
$.error.details (oneOf 3) | object | Да | required: ["retry_after"]; Дополнительные поля запрещены | |
$.error.details (oneOf 3).retry_after | integer | Да | minimum: 0 | |
$.error.request_id | string / null | Да | format: "uuid" | UUID для диагностики или null без контекста. При HTTP replay тело может содержать исходный ID, заголовок X-Request-ID относится к текущей попытке. |
default (422)
json
{
"error": {
"code": "validation_failed",
"message": "A valid Idempotency-Key header is required.",
"details": [],
"request_id": "11111111-1111-4111-8111-111111111111"
}
}fields (422)
json
{
"error": {
"code": "validation_failed",
"message": "The name field is required.",
"details": {
"name": [
"The name field is required."
]
},
"request_id": "11111111-1111-4111-8111-111111111111"
}
}HTTP 429
Квота API-ключа исчерпана. Ждите Retry-After. Собственный 429 не содержит X-RateLimit-Limit/Remaining/Reset.
Заголовок X-Request-ID: UUID текущего запроса. Корректный входной X-Request-ID принимается в нижнем регистре, иначе создаётся новый. Не гарантирован для proxy/неизвестного маршрута.
| Поле | Тип | Обязательно в родителе | Ограничения | Описание |
|---|---|---|---|---|
$ | string | Да | format: "uuid" |
Заголовок Retry-After: На 429 API: целые секунды до сброса окна, не дата/Unix timestamp. При 0 добавьте jitter. Proxy может вернуть HTTP-date.
| Поле | Тип | Обязательно в родителе | Ограничения | Описание |
|---|---|---|---|---|
$ | string | Да | pattern: "^[0-9]+$" |
text
Retry-After: 42Content-Type: application/json
| Поле | Тип | Обязательно в родителе | Ограничения | Описание |
|---|---|---|---|---|
$ | object | Да | required: ["error"] | Собственный JSON-конверт API, не RFC 9457. Структура и code не зависят от языка; message и details предназначены человеку. До выбора маршрута и на proxy этот конверт не гарантирован. Схема: ErrorEnvelope |
$.error | object | Да | required: ["code","message","details","request_id"] | |
$.error.code | string | Да | Машинный код; не извлекайте причину из message. | |
$.error.message | string | Да | Человекочитаемое, частично локализуемое сообщение; не стабильный идентификатор ошибки. | |
$.error.details | oneOf | Да | Без подробностей возвращается [], не null и не {}. Валидация: {поле:[сообщения]}. Квота: {retry_after:секунды}. | |
$.error.details (oneOf 1) | array | Да | maxItems: 0 | |
$.error.details (oneOf 2) | object | Да | ||
$.error.details (oneOf 2).* | array | Нет | ||
$.error.details (oneOf 2).*[] | string | Да | ||
$.error.details (oneOf 3) | object | Да | required: ["retry_after"]; Дополнительные поля запрещены | |
$.error.details (oneOf 3).retry_after | integer | Да | minimum: 0 | |
$.error.request_id | string / null | Да | format: "uuid" | UUID для диагностики или null без контекста. При HTTP replay тело может содержать исходный ID, заголовок X-Request-ID относится к текущей попытке. |
default (429)
json
{
"error": {
"code": "rate_limit_exceeded",
"message": "API rate limit exceeded.",
"details": {
"retry_after": 42
},
"request_id": "11111111-1111-4111-8111-111111111111"
}
}HTTP 500
Этот HTTP-ответ не определяет результат записи. Сначала сверка; повтор только с прежним ключом и точным телом.
Заголовок X-Request-ID: UUID текущего запроса. Корректный входной X-Request-ID принимается в нижнем регистре, иначе создаётся новый. Не гарантирован для proxy/неизвестного маршрута.
| Поле | Тип | Обязательно в родителе | Ограничения | Описание |
|---|---|---|---|---|
$ | string | Да | format: "uuid" |
Content-Type: application/json
| Поле | Тип | Обязательно в родителе | Ограничения | Описание |
|---|---|---|---|---|
$ | object | Да | required: ["error"] | Собственный JSON-конверт API, не RFC 9457. Структура и code не зависят от языка; message и details предназначены человеку. До выбора маршрута и на proxy этот конверт не гарантирован. Схема: ErrorEnvelope |
$.error | object | Да | required: ["code","message","details","request_id"] | |
$.error.code | string | Да | Машинный код; не извлекайте причину из message. | |
$.error.message | string | Да | Человекочитаемое, частично локализуемое сообщение; не стабильный идентификатор ошибки. | |
$.error.details | oneOf | Да | Без подробностей возвращается [], не null и не {}. Валидация: {поле:[сообщения]}. Квота: {retry_after:секунды}. | |
$.error.details (oneOf 1) | array | Да | maxItems: 0 | |
$.error.details (oneOf 2) | object | Да | ||
$.error.details (oneOf 2).* | array | Нет | ||
$.error.details (oneOf 2).*[] | string | Да | ||
$.error.details (oneOf 3) | object | Да | required: ["retry_after"]; Дополнительные поля запрещены | |
$.error.details (oneOf 3).retry_after | integer | Да | minimum: 0 | |
$.error.request_id | string / null | Да | format: "uuid" | UUID для диагностики или null без контекста. При HTTP replay тело может содержать исходный ID, заголовок X-Request-ID относится к текущей попытке. |
default (500)
json
{
"error": {
"code": "internal_error",
"message": "The request could not be completed.",
"details": [],
"request_id": "11111111-1111-4111-8111-111111111111"
}
}Дополнительные примеры из контракта
cURL
bash
BASE_URL='https://api.b.websoft.kz/api/reseller/v1'
# BASE_URL включает /api/reseller/v1. Подставьте реальные UUID своего окружения.
# Для нового намеренного действия задайте новый IDEMPOTENCY_KEY; для повтора сохраните прежний.
curl --request GET "$BASE_URL/domains/019a1234-1000-7000-8000-000000000001/registrant-verification/payload" \
--header "Authorization: Bearer $RESELLER_API_TOKEN" \
--header "Accept: application/json"Машиночитаемый контракт
OpenAPI JSON · OpenAPI YAML · Markdown этой операции
Источники проверки контракта
routes/api/reseller/v1.phpapp/Application/ResellerApi/V1/Controller/DomainController.php