For AI agents: markdown of this page — /docs-content-en/infra/domain/attach.md documentation index — /llms.txt
Attach a domain
POST /v1/infra/servers/:id/domain
Assigns your domain name to the application on the server and begins the first step of attachment: creating DNS records at the domain's DNS provider. The application starts accepting requests on the domain only after the records are verified successfully. A server can have only one domain.
Who can make the call — Access. The purpose of the records and the difference between host and name — DNS records.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
Parameterid |
Inpath | Typestring (UUID) | Requiredyes | DescriptionServer ID. Source — the id field from the server list |
Request fields (body)
| Field | Type | Required | Description |
|---|---|---|---|
Fieldfqdn |
Typestring | Requiredyes | DescriptionDomain name without scheme, port or path, for example app.example.com. A subdomain or an apex domain is accepted. Case is ignored, leading and trailing spaces are trimmed. The name consists of at least two dot-separated labels; each label is 1 to 63 Latin letters, digits and hyphens, and a hyphen is not allowed at the start or the end of a label |
Examples
curl — personal key
curl -X POST "https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/domain" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"fqdn": "app.example.com"
}'
curl — OAuth application
curl -X POST "https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/domain" \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Authorization: Bearer USER_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"fqdn": "app.example.com"
}'
JavaScript — personal key
const res = await fetch(
`https://vibecode.bitrix24.com/v1/infra/servers/${serverId}/domain`,
{
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fqdn: 'app.example.com',
}),
}
)
const { data } = await res.json()
for (const record of data.instructions) {
console.log(`${record.type} ${record.name} → ${record.value}`)
}
JavaScript — OAuth application
const res = await fetch(
`https://vibecode.bitrix24.com/v1/infra/servers/${serverId}/domain`,
{
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_APP_KEY',
'Authorization': 'Bearer USER_SESSION_TOKEN',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fqdn: 'app.example.com',
}),
}
)
Response fields
| Field | Type | Description |
|---|---|---|
Fieldsuccess |
Typeboolean | DescriptionAlways true on success |
Fielddata.domain.id |
Typestring (UUID) | DescriptionDomain attachment ID |
Fielddata.domain.fqdn |
Typestring | DescriptionThe attached name in lowercase |
Fielddata.domain.isApex |
Typeboolean | Descriptiontrue — the apex domain of the zone, false — a subdomain |
Fielddata.domain.status |
Typestring | DescriptionDomain status. After attachment — "PENDING_VERIFICATION". All values — Domain statuses |
Fielddata.domain.source |
Typestring | DescriptionOrigin of the domain. "BYO" — the domain is yours and was connected by you |
Fielddata.domain.dnsMode |
Typestring | DescriptionWho maintains the DNS records. "USER_DNS" — you, at your DNS provider |
Fielddata.domain.verifiedAt |
Typestring (ISO 8601) | null | DescriptionTime of the successful verification after which the domain became ACTIVE. After attachment — null |
Fielddata.domain.lastVerifiedAt |
Typestring (ISO 8601) | null | DescriptionTime of the last record verification, including a failed one. After attachment — null |
Fielddata.domain.createdAt |
Typestring (ISO 8601) | DescriptionTime of attachment |
Fielddata.domain.certIssued |
Typeboolean | DescriptionWhether the certificate for https has been issued. After attachment — false |
Fielddata.instructions |
Typearray | DescriptionTwo DNS records to create at the domain's DNS provider |
Fielddata.instructions[].type |
Typestring | DescriptionRecord type: "TXT", "CNAME" or "A" |
Fielddata.instructions[].host |
Typestring | DescriptionFull record name |
Fielddata.instructions[].zone |
Typestring | DescriptionZone in which the record is created |
Fielddata.instructions[].name |
Typestring | DescriptionRecord name relative to zone — the value entered in the "name" field of the DNS provider's panel. For the zone itself — "@" |
Fielddata.instructions[].value |
Typestring | DescriptionRecord value |
Response example
Response 201 for attaching a subdomain:
{
"success": true,
"data": {
"domain": {
"id": "bea9db66-411b-432e-a365-283a74f5714f",
"fqdn": "app.example.com",
"isApex": false,
"status": "PENDING_VERIFICATION",
"source": "BYO",
"dnsMode": "USER_DNS",
"verifiedAt": null,
"lastVerifiedAt": null,
"createdAt": "2026-10-05T12:53:02.616Z",
"certIssued": false
},
"instructions": [
{
"type": "TXT",
"host": "_vibecode-challenge.app.example.com",
"zone": "example.com",
"name": "_vibecode-challenge.app",
"value": "vibe-verify=f36ac31f213d1e3630c75f000b6631f5"
},
{
"type": "CNAME",
"host": "app.example.com",
"zone": "example.com",
"name": "app",
"value": "lb.bitrix24.site"
}
]
}
}
The apex domain example.com would get "isApex": true, and the second record would be "type": "A" with "name": "@" and an IPv4 address in value. The rest of the response shape does not change.
Error response example
409 — the name is already attached to another server on the platform:
{
"success": false,
"error": {
"code": "DOMAIN_TAKEN",
"message": "This domain is already in use"
}
}
Errors
| HTTP | Code | Description |
|---|---|---|
| HTTP400 | CodeINVALID_REQUEST |
DescriptionThe fqdn field is missing or is not a domain name |
| HTTP400 | CodeFQDN_INVALID |
DescriptionAn IP address was passed in fqdn |
| HTTP400 | CodeFQDN_RESERVED |
DescriptionThe name belongs to the platform: it is the platform's domain or its subdomain |
| HTTP400 | CodeFQDN_FORBIDDEN |
DescriptionThe name is forbidden: it contains the substring bitrix |
| HTTP400 | CodeINVALID_JSON_BODY |
DescriptionThe request body cannot be parsed as JSON |
| HTTP409 | CodeCUSTOM_DOMAIN_GALAXY_MOVE_ACTIVE |
DescriptionThe server is moving to a Galaxy: it is the old server of the move or the place prepared for it. Attach the domain after the move is completed or cancelled |
| HTTP409 | CodeCUSTOM_DOMAIN_LIMIT |
DescriptionThe server already has a domain, including one with the same name. Detach it first |
| HTTP409 | CodeDOMAIN_TAKEN |
DescriptionThe name is attached to another server on the platform |
| HTTP409 | CodeDOMAIN_TAKEN_EXTERNAL |
DescriptionThe name is connected to another Bitrix24 product. Disconnect it there and repeat the call |
| HTTP503 | CodeCUSTOM_DOMAIN_REGISTRY_UNAVAILABLE |
DescriptionThe domain registry did not respond. The domain is not attached; repeat the call later |
| HTTP403 | CodeCUSTOM_DOMAINS_DISABLED |
DescriptionCustom domains are not enabled for the server owner |
| 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 |
| HTTP401 | CodeMISSING_API_KEY |
DescriptionThe X-Api-Key header is missing |
| HTTP401 | CodeINVALID_API_KEY |
DescriptionThe key is not recognized — no such key exists on the platform |
| HTTP403 | CodeINFRA_SCOPE_REQUIRED |
DescriptionThe key lacks the vibe:infra scope |
| 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 and cannot perform write operations |
| HTTP404 | CodeSERVER_NOT_FOUND |
DescriptionNo server with this id exists, or it is managed by a key other than the one used for the call |
| HTTP429 | CodeRATE_LIMITED |
DescriptionThe limit of 5 requests per minute for this endpoint was exceeded. The limit is counted per key owner, not per individual key. The exact value arrives in the x-ratelimit-limit header (the cap is divided across replicas) |
| HTTP503 | Codeuser_self_deletion_pending |
DescriptionThe key owner requested deletion of their account on the portal. Access is restored once the deletion is cancelled |
Full list of common API errors — Errors.
Known specifics
- Order of checks. The key scope and server ownership are checked first, then the three access conditions for custom domains, and only after them the request body. So a request with an invalid
fqdnfrom an account that has no access to custom domains gets403, not400. The exception isINVALID_JSON_BODY: the body is parsed before the key check, so a body with a JSON syntax error gets400before all other refusals. TheFQDN_FORBIDDENandDOMAIN_TAKEN_EXTERNALrefusals come last: they are issued by the domain registry, which the platform queries after its own checks. - The name is fully secured only at verification. Attachment closes the name to other servers on the platform, but in the Bitrix24 domain registry it is taken only after a successful verification. If the name is connected to another Bitrix24 product between attachment and verification, the verification returns the
DOMAIN_TAKEN_EXTERNALrefusal instead. - The records can be fetched again. The
instructionsarray with the sameTXTvalue is returned at any time byGET /v1/infra/servers/:id/domain; storing the attachment response is not required.