For AI agents: markdown of this page — /docs-content-en/infra/servers/b24-catalog-publish.md documentation index — /llms.txt
Publish the app to the Bitrix24 catalog
POST /v1/infra/servers/:id/b24-catalog/publish
Creates the app card in the Vibecode apps catalog on the Bitrix24 account. Before this call the card appeared only after a successful deployment, so an app brought up any other way was missing from the catalog, and granting access to employees did not surface it.
Editing the access policy or the access list does not create a card — it updates an existing one. Publishing stays a separate action: the card appears in the catalog for the owner and for the employees who already have access.
This is not REST app publishing and not an interface placement — those are POST /v1/apps/:id/publish and placements.
Parameters
| Parameter | In | Type | Req. | Default | Description |
|---|---|---|---|---|---|
id |
path | string (UUID) | yes | — | ID of a server that has no catalog card yet |
The request body may be empty or {}. Unknown fields are ignored.
Examples
curl — personal key
curl -X POST -H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" -d '{}' \
https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/b24-catalog/publish
curl — OAuth application
curl -X POST -H "X-Api-Key: YOUR_APP_KEY" \
-H "Authorization: Bearer USER_SESSION_TOKEN" \
-H "Content-Type: application/json" -d '{}' \
https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/b24-catalog/publish
JavaScript — personal key
const res = await fetch(
`https://vibecode.bitrix24.com/v1/infra/servers/${serverId}/b24-catalog/publish`,
{
method: 'POST',
headers: { 'X-Api-Key': 'YOUR_API_KEY', 'Content-Type': 'application/json' },
body: '{}',
}
)
const body = await res.json()
if (!body.success) {
// CATALOG_ALREADY_PUBLISHED means the card is already there — do not repeat
console.error(body.error.code, body.error.message)
throw new Error(body.error.code)
}
// The card appears in the catalog after the next synchronization cycle
console.log('Publication queued')
JavaScript — OAuth application
const res = await fetch(
`https://vibecode.bitrix24.com/v1/infra/servers/${serverId}/b24-catalog/publish`,
{
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_APP_KEY',
'Authorization': 'Bearer USER_SESSION_TOKEN',
'Content-Type': 'application/json',
},
body: '{}',
}
)
Response fields
| Field | Type | Description |
|---|---|---|
success |
boolean | true when the publication is accepted |
data.queued |
boolean | true — the publication is queued. The card is created by background synchronization, not by this call |
data.op |
string | The queued operation. For this endpoint it is always ADD |
The publication state is read through GET /v1/infra/servers/:id in the b24CatalogSync block:
| Field | Type | Description |
|---|---|---|
b24CatalogSync.itemId |
number | null | ID of the card in the Bitrix24 catalog. null — there is no card. This is the "published" signal, the other fields describe the process |
b24CatalogSync.status |
string | IDLE — queued or nothing to synchronize, PENDING — being processed, SYNCED — the card is synchronized, FAILED — synchronization did not succeed, ORPHANED — the card was removed on the Bitrix24 side |
b24CatalogSync.pendingOp |
string | null | The queued operation: ADD, UPDATE, DELETE. null — the queue is empty |
b24CatalogSync.attempts |
number | Number of synchronization attempts for the current operation |
b24CatalogSync.eligible |
boolean | false when a card is impossible: the server has no subdomain, no app has been deployed to it yet, it is an agent runtime or a galaxy host, it was switched to OPEN mode, it has no managing key or account, or catalog synchronization is switched off on the platform side. With false the publish call answers 400 CATALOG_NOT_ELIGIBLE |
A value of eligible: true means "a card is possible", not "publishing will succeed": for an app whose card already exists the call answers 409 CATALOG_ALREADY_PUBLISHED.
Response example
{
"success": true,
"data": {
"queued": true,
"op": "ADD"
}
}
Error response example
409 — the catalog card already exists:
{
"success": false,
"error": {
"code": "CATALOG_ALREADY_PUBLISHED",
"message": "This server already has a Bitrix24 catalog card."
}
}
Errors
| HTTP | Code | Description |
|---|---|---|
| 401 | MISSING_API_KEY |
The X-Api-Key header is missing |
| 401 | INVALID_API_KEY |
The API key is wrong or expired |
| 400 | CATALOG_NOT_ELIGIBLE |
A card is impossible for this server: no subdomain, no app deployed yet, an agent runtime or a galaxy host, the server is in OPEN mode, it has no managing key or account, or catalog synchronization is switched off on the platform side |
| 400 | CATALOG_ORPHANED |
The card was removed on the Bitrix24 side. Restoring it is available in the Vibecode dashboard, not on this endpoint |
| 403 | INFRA_FORBIDDEN_FOR_COWORK_KEY |
The call was made with a Cowork/Code key — such a key works with data only and cannot perform write operations. To issue a key that can, see Project key for deploy |
| 409 | CATALOG_ALREADY_PUBLISHED |
The card already exists. There is no need to repeat the call |
| 409 | CATALOG_DELETE_PENDING |
A catalog delete is already queued — publishing is unavailable in this state |
| 404 | NOT_FOUND |
The server is deleted or managed by another API key |
| 429 | RATE_LIMITED |
Request limit exceeded. The response carries a Retry-After header with the recommended pause |
The full list of shared API errors — Errors.
Known specifics
- The call queues a task instead of creating the card. The card is created by background synchronization, so
itemIdinGET /v1/infra/servers/:idis not filled at response time. Track readiness by polling:b24CatalogSync.itemIdstops beingnullandstatusbecomesSYNCED. - Repeating the call while the publication is queued is safe. The answer is
200again and no second card is created. - A managing key is required. Publishing runs on behalf of the owner of the server's managing key, so a server that lost its key answers
400 CATALOG_NOT_ELIGIBLE. The key is restored in your Vibecode account. - Granting access does not create the card. The order is: publish first, then the access policy and the access list. The reverse order also works — after publishing, the card picks up the access already granted on the first synchronization.
- Deploy first, publish second. The subdomain is minted when the server is created, and an app only appears on it after a deploy, so a server with no deploy on record returns
eligible: falseand the call answers400 CATALOG_NOT_ELIGIBLE. Otherwise the catalog card would lead to an address where nothing answers, and the only way to remove it would be to delete the server. The platform also holds no deploy record when the app arrived not throughdeploybut via a file upload, a command or SSH — such a server gets400 CATALOG_NOT_ELIGIBLEtoo, even though the app is running on it. The only way around is a real deploy, and by default (cleanDeploy) it wipes the app directory, so save whatever is already there first. - Catalog synchronization can be switched off on the platform side. In that state
eligiblecomes backfalseand the publish call answers400 CATALOG_NOT_ELIGIBLE: nothing would process the queue, so the platform refuses up front instead of leaving the card "being added" indefinitely.