For AI agents: markdown of this page — /docs-content-en/infra/domain/verify.md documentation index — /llms.txt
Verify the domain
POST /v1/infra/servers/:id/domain/verify
Checks both DNS records of the attached domain and, if they are correct, enables the domain: the application starts responding on it. The same call re-enables a domain that was disabled after a failed check.
Who can make the call — Access.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
Parameterid |
Inpath | Typestring (UUID) | Requiredyes | DescriptionServer ID. Source — the id field from the server list |
The request body is empty.
Examples
curl — personal key
curl -X POST "https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/domain/verify" \
-H "X-Api-Key: YOUR_API_KEY"
curl — OAuth application
curl -X POST "https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/domain/verify" \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Authorization: Bearer USER_SESSION_TOKEN"
JavaScript — personal key
const res = await fetch(
`https://vibecode.bitrix24.com/v1/infra/servers/${serverId}/domain/verify`,
{ method: 'POST', headers: { 'X-Api-Key': 'YOUR_API_KEY' } }
)
const body = await res.json()
if (body.success) {
console.log(`Domain enabled: ${body.data.domain.fqdn}`)
} else if (body.error.code === 'DOMAIN_VERIFICATION_FAILED') {
// Show which record failed the check
for (const record of body.error.details.records) {
if (record.status !== 'ok') console.log(`${record.type} ${record.host}: ${record.status}`)
}
} else {
throw new Error(body.error.code)
}
JavaScript — OAuth application
const res = await fetch(
`https://vibecode.bitrix24.com/v1/infra/servers/${serverId}/domain/verify`,
{
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_APP_KEY',
'Authorization': 'Bearer USER_SESSION_TOKEN',
},
}
)
Response fields
| Field | Type | Description |
|---|---|---|
Fieldsuccess |
Typeboolean | Descriptiontrue when both records are correct and the domain is enabled |
Fielddata.domain.id |
Typestring (UUID) | DescriptionDomain attachment ID |
Fielddata.domain.fqdn |
Typestring | DescriptionDomain name in lowercase |
Fielddata.domain.isApex |
Typeboolean | Descriptiontrue — apex domain, false — subdomain |
Fielddata.domain.status |
Typestring | DescriptionDomain status, ACTIVE in this response. All values — Domain statuses |
Fielddata.domain.source |
Typestring | DescriptionOrigin of the domain. BYO — a domain you own |
Fielddata.domain.dnsMode |
Typestring | DescriptionWho maintains the DNS records. USER_DNS — you, at your DNS provider |
Fielddata.domain.verifiedAt |
Typestring | null | DescriptionTime of the last successful check, ISO 8601 |
Fielddata.domain.lastVerifiedAt |
Typestring | null | DescriptionTime of the last check, successful or not, ISO 8601 |
Fielddata.domain.createdAt |
Typestring | DescriptionTime the domain was attached, ISO 8601 |
Fielddata.domain.certIssued |
Typeboolean | DescriptionWhether the certificate for https has been issued |
Fielddata.records |
Typearray | DescriptionResult for each record: TXT comes first, CNAME or A second |
Fielddata.records[].type |
Typestring | DescriptionRecord type: TXT, CNAME or A |
Fielddata.records[].host |
Typestring | DescriptionFull record name |
Fielddata.records[].status |
Typestring | Descriptionok — the record is correct, missing — not found, wrong — found with a different value. In this response both records are ok |
Fielddata.records[].found |
Typestring[] | DescriptionValues the check read from DNS. A CNAME record may carry IP addresses here: that is how a DNS provider that flattens CNAME into addresses responds |
Response example
{
"success": true,
"data": {
"domain": {
"id": "bea9db66-411b-432e-a365-283a74f5714f",
"fqdn": "app.example.com",
"isApex": false,
"status": "ACTIVE",
"source": "BYO",
"dnsMode": "USER_DNS",
"verifiedAt": "2026-10-05T12:53:02.655Z",
"lastVerifiedAt": "2026-10-05T12:53:02.653Z",
"createdAt": "2026-10-05T12:53:02.616Z",
"certIssued": false
},
"records": [
{
"type": "TXT",
"host": "_vibecode-challenge.app.example.com",
"status": "ok",
"found": [
"vibe-verify=f36ac31f213d1e3630c75f000b6631f5"
]
},
{
"type": "CNAME",
"host": "app.example.com",
"status": "ok",
"found": [
"lb.bitrix24.site"
]
}
]
}
}
Error response example
422 — the TXT record is correct, but CNAME points to a different address:
{
"success": false,
"error": {
"code": "DOMAIN_VERIFICATION_FAILED",
"message": "Domain verification failed",
"details": {
"reason": "NOT_POINTING_TO_US",
"records": [
{
"type": "TXT",
"host": "_vibecode-challenge.app.example.com",
"status": "ok",
"found": [
"vibe-verify=f36ac31f213d1e3630c75f000b6631f5"
]
},
{
"type": "CNAME",
"host": "app.example.com",
"status": "wrong",
"found": [
"other.example.net"
]
}
],
"domain": {
"id": "bea9db66-411b-432e-a365-283a74f5714f",
"fqdn": "app.example.com",
"isApex": false,
"status": "FAILED",
"source": "BYO",
"dnsMode": "USER_DNS",
"verifiedAt": null,
"lastVerifiedAt": "2026-10-05T12:53:02.648Z",
"createdAt": "2026-10-05T12:53:02.616Z",
"certIssued": false
},
"hint": "Fix the records whose status is \"missing\" or \"wrong\" (see records), then repeat the check. A record that is already published is read from the authoritative name servers of the zone, so it is seen at once. If the check fails right after you edited DNS, the DNS provider has not published the change yet, or a resolver still returns the previous answer until the record TTL expires — for a name that did not exist before, that is the negative-cache TTL of the zone (SOA minimum, up to an hour). Repeating the check immediately returns the same result: wait a few minutes between attempts. The check is limited to 5 calls per minute."
}
}
}
The error.details fields of this refusal:
| Field | Type | Description |
|---|---|---|
Fielderror.details.reason |
Typestring | DescriptionThe first record that failed. OWNERSHIP_TXT_MISSING — the TXT record is not found or incorrect, NOT_POINTING_TO_US — TXT is correct, but the domain does not point to the platform |
Fielderror.details.records |
Typearray | DescriptionResult for both records, with the same fields as data.records. Until the TXT record is confirmed, found is empty for both records |
Fielderror.details.records[].hint |
Typestring | DescriptionNot present on every record. DOUBLED_ZONE — the record is missing, but it was found under a name with the zone appended twice: host was entered in the DNS panel instead of name, see DNS records. HAS_AAAA — the domain name has an AAAA record, delete it |
Fielderror.details.domain |
Typeobject | DescriptionThe domain after the check, with the same fields as data.domain. Status — FAILED |
Fielderror.details.hint |
Typestring | DescriptionText in English: what to fix and when to repeat the check |
Errors
| HTTP | Code | Description |
|---|---|---|
| HTTP400 | CodeFQDN_FORBIDDEN |
DescriptionBoth records are correct, but the domain name is not allowed: it must not contain bitrix. The domain moves to FAILED |
| HTTP401 | CodeMISSING_API_KEY |
DescriptionThe X-Api-Key header is not passed |
| HTTP401 | CodeINVALID_API_KEY |
DescriptionThe key is not recognized: no such string exists on the platform |
| HTTP403 | CodeINFRA_SCOPE_REQUIRED |
DescriptionThe key lacks the vibe:infra scope. Checked before the server lookup |
| HTTP403 | CodeINFRA_FORBIDDEN_FOR_COWORK_KEY |
DescriptionThe call was made with a Cowork/Code key — such a key works with data only and cannot perform write operations. What to do — Project key for deploy |
| HTTP403 | CodeWRITE_BLOCKED_READONLY_KEY |
DescriptionThe key is in read-only mode — write operations are blocked for it |
| HTTP403 | CodeCUSTOM_DOMAINS_DISABLED |
DescriptionCustom domains are not enabled for the server owner. Conditions — Access |
| HTTP403 | CodeCUSTOM_DOMAINS_ZONE_UNSUPPORTED |
DescriptionThe region of the Bitrix24 account does not support custom domains |
| HTTP403 | CodeCUSTOM_DOMAINS_REQUIRE_COMMERCIAL |
DescriptionThe Bitrix24 account has no commercial plan |
| HTTP404 | CodeSERVER_NOT_FOUND |
DescriptionThe server is not found, is deleted, or is managed by another key |
| HTTP404 | CodeNO_DOMAIN |
DescriptionNo domain is attached to the server. Checked before the access conditions that return 403 |
| HTTP409 | CodeSERVER_HAS_NO_APP_HOST |
DescriptionBoth records are correct, but the server has no public application address yet. The domain status does not change. Deploy the application and repeat the check |
| HTTP409 | CodeDOMAIN_TAKEN_EXTERNAL |
DescriptionBoth records are correct, but after the domain was attached the name was connected to another Bitrix24 product. The domain moves to FAILED. Disconnect the name there and repeat the check |
| HTTP422 | CodeDOMAIN_VERIFICATION_FAILED |
DescriptionA record is not found or incorrect. The domain moves to FAILED. Which record failed — in error.details.records |
| HTTP429 | CodeRATE_LIMITED |
DescriptionThe limit of 5 requests per minute for this endpoint is exceeded. The limit is counted per key owner, not per individual key. The exact value is in the x-ratelimit-limit header (the cap is divided across replicas) |
| HTTP503 | CodeCUSTOM_DOMAIN_REGISTRY_UNAVAILABLE |
DescriptionThe domain registry did not respond, or the platform could not read the address it compares the records against. The domain status stays as it was. Repeat the call later |
| HTTP503 | Codeuser_self_deletion_pending |
DescriptionThe key owner requested deletion of their account on the portal. Access is restored once the deletion is cancelled |
The full list of common API errors — Errors.
Known specifics
- Repeating the check right after
422returns the same result. A record the DNS provider has already published is read from the authoritative servers of the zone and is seen at once. If the refusal came right after a DNS edit, the provider has not published the change yet, or a caching DNS server still returns the previous answer until the record's time to live (TTL) expires. For a name that did not exist before, that is up to an hour. Wait a few minutes between attempts. - Checking a working domain can disable it. The call is also allowed for a domain in the
ACTIVEstatus. If the records are incorrect by then, the domain moves toFAILED, and the application stops responding on it until the next successful check. - The certificate is issued after the response. When the domain reaches
ACTIVEfrom another status,certIssuedisfalse.httpsreadiness is shown byGET /v1/infra/servers/:id/domain.