For AI agents: markdown of this page — /docs-content-en/apps.md documentation index — /llms.txt
Applications
A Vibecode OAuth application is a record in the catalog of your Bitrix24 account, through which your code is embedded into the interface: CRM cards, the left menu, chats, and other placements. The application lifecycle runs through four steps: create → deploy code → set the URL → publish. Before publishing, only the author sees the application, its appUrl is empty, and the placements list is empty — this is a normal state, not an error.
Base URL: https://vibecode.bitrix24.com/v1 | Authorization: X-Api-Key | Scope for publishing: placement
Which key to choose | Application states | Full cycle | Replacing and merging applications | Placements | Known specifics | Windows / PowerShell and UTF-8 | Error codes | Endpoint reference
Which key to choose
This section works with two types of keys.
A personal API key vibe_api_… is bound to one user and one Bitrix24 account and carries its own webhook for that account. Requests run on behalf of the key owner, there is a single header — X-Api-Key: vibe_api_…, and no session token is needed. Use this key to manage application records: create, read, update, and delete. Creating one registers a real local OAuth application on the account, and you can create several such applications under a single personal key.
An authorization key vibe_app_… is the key of the OAuth application itself. For it to act on behalf of a specific user, the header Authorization: Bearer <session_token> is required alongside X-Api-Key: vibe_app_… — the platform uses it to look up that user's personal OAuth token. This is the model for applications that act on behalf of different users of the account where the application is registered.
Placements and publishing. Publishing can be started with either key, but the placement binding itself is performed by the application's OAuth token — so the application must be authorized on the account via OAuth once, otherwise publishing returns NO_USER_TOKEN. For an application that opens inside a Bitrix24 account and acts on behalf of the already signed-in user, only the OAuth application model vibe_app_… works — a personal key is not enough.
How many applications you can create. The number of keys per Bitrix24 account user is limited — 10 by default. Applications consume the same counter as personal keys, and there is no separate limit for applications. When the limit is reached, creation returns 409 KEY_LIMIT_REACHED, and the account administrator can raise it. Details — How many keys you can create.
A detailed description of key types, formats, and obtaining session_token — Keys and authorization.
Application states
| Status | Meaning | Visible in catalog |
|---|---|---|
PRIVATE |
Default state right after creation. Placements are not bound to the account | Author only |
PUBLISHED |
Application published, placements bound to the account | All account employees |
UNPUBLISHED |
Unpublished, placements unbound, but the catalog record is kept | Everyone, inactive |
Transitions: PRIVATE → PUBLISHED → UNPUBLISHED → PUBLISHED — the last arrow is republishing. Unpublishing moves the application to UNPUBLISHED, not back to PRIVATE: catalog metadata is kept for republishing.
The catalog status is returned in the catalogStatus field — one of PRIVATE / PUBLISHED / UNPUBLISHED. The publication date, if the application was ever published, is in publishedAt. Determine the status by catalogStatus, not by the placements array: an empty array does not distinguish PRIVATE from UNPUBLISHED, and an unpublished application may keep its previously bound codes.
Why `appUrl: null` and empty `placements` before publishing
A common question: the application is created, the code is deployed, GET /v1/me shows capabilities.apps.publish.available: true, but application data returns appUrl: null and an empty placements array, and the application is not in the catalog. This is expected while the application is in the PRIVATE status:
appUrlis empty until you set it via updating the application or pass it in the publish body.placementsis empty because placements are bound to the account only at the moment of publishing.catalogStatusisPRIVATEandpublishedAtisnulluntil the application is published.- The application appears in the catalog only after publishing.
`appUrl` and `handlerUrl` are different addresses
handlerUrl is set by the platform and cannot be changed — OAuth callbacks and placement opening go through it. appUrl is your address on Black Hole — the platform redirects there when a placement is opened. You set this address yourself.
Full cycle
Step 1. Create the application
Creation registers an OAuth application on the account and returns the key once — in the rawKey field of the response. Save it immediately: the key is not shown again. For subsequent publishing, placement is required in the scope set.
curl -X POST "https://vibecode.bitrix24.com/v1/apps" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Sales dashboard",
"scopes": ["crm", "user", "placement"],
"appUrl": "https://app-abc12345.vibecode.bitrix24.com"
}'
Step 2. Deploy the code and set the URL
Deploy the application to a Black Hole server (Deploy API) and make sure appUrl points to its address. If the address was not set at creation, set it via updating:
curl -X PATCH "https://vibecode.bitrix24.com/v1/apps/APP_ID" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "appUrl": "https://app-abc12345.vibecode.bitrix24.com" }'
Step 3. Publish
Publishing moves the application to PUBLISHED, binds placements to the account, and makes the application visible to all employees. Before publishing, the application must be authorized on the account via OAuth, and the key's scope set must include placement.
curl -X POST "https://vibecode.bitrix24.com/v1/apps/APP_ID/publish" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "placements": ["CRM_DEAL_DETAIL_TAB"] }'
The reverse path — unpublishing and deletion — is in Replacing and merging applications.
Replacing and merging applications
An application is taken out of service with two operations: unpublishing removes it from the interface points of the account, and deletion hides the application from the catalog and the account handler, and revokes its authorization key. There is no need to unbind placements one by one beforehand — unpublishing removes them.
Take an application out of service
- Unpublish —
POST /v1/apps/:id/unpublish. The application moves toUNPUBLISHED, placements are unbound from the account, and the catalog record is preserved. The operation does not guarantee that the account confirmed the removal — see Unpublish for details. The call accepts only an application in thePUBLISHEDstatus — for any other status the response is404 NOT_FOUND. - Rebind servers to another key if the application key has running servers. While they exist, deletion responds with
409 APP_HAS_ACTIVE_SERVERSand lists the blocking servers indetails.servers. - Delete the application —
DELETE /v1/apps/:id, response204 No Content. The application authorization key stops working immediately: a request with it responds with401 KEY_INACTIVE. Deleting the same identifier again responds with404 APP_NOT_FOUND.
# 1. Unpublish — placements will be unbound from the account
curl -X POST "https://vibecode.bitrix24.com/v1/apps/APP_ID/unpublish" \
-H "X-Api-Key: YOUR_API_KEY"
# 2. Delete the application and revoke its authorization key
curl -X DELETE "https://vibecode.bitrix24.com/v1/apps/APP_ID" \
-H "X-Api-Key: YOUR_API_KEY"
If the application may be needed again, stop at the first step: publishing it again with an empty body returns it to the catalog.
Manual placement unbinding serves a different purpose — removing a single interface point from an application that stays in service.
Replace one application with another
A new application is a separate registration on the account with its own authorization key and its own placement list. Follow this order so employees never lose access:
- Create an application with the scope set you need, deploy the code, set
appUrl. - Authorize the new application on the account via OAuth — without that, publishing responds with
400 NO_USER_TOKEN. - Publish the new application with the same placement codes.
- Unpublish and delete the old application following the steps above.
Merge several applications into one
The scope set is defined when the application is created. A scope cannot be added to an existing application: updating the application with an extended set responds with 403 OAUTH_SCOPE_CHANGE_REQUIRES_REISSUE. The same call does allow narrowing the set. A merge is therefore a new application with the combined scope set, not a rework of one of the existing ones.
The order for merging three applications into one:
- Collect the scope sets and placement codes of the three applications into a single list. The current values come from the application list.
- Create a new application with the combined scope set, deploy the code, set
appUrl. - Authorize the new application on the account via OAuth and publish it with all placement codes at once.
- Move the integrations to the key of the new application. The keys of the three old applications stop working the moment those applications are deleted.
- Unpublish and delete the three old applications.
During the transition, the old and the new applications occupy slots in the same key counter. If it is exhausted, creation responds with 409 KEY_LIMIT_REACHED — free a slot by deleting one of the old applications.
Placements
A placement is a point in the Bitrix24 interface where your application opens: a tab in a deal card, a left-menu item, a button on a list toolbar, a panel in a chat. Publishing binds the placements listed in the placements field, while separate bind and unbind calls let you manage them one at a time and change the label and the handler address without republishing.
A few conditions must be met before the first bind — each unmet condition returns its own error code.
| Condition | What happens if it is not met |
|---|---|
The application authorization key vibe_app_…. A personal key cannot bind a placement |
400 OAUTH_APP_REQUIRED |
The placement scope on the key — required for binding and unbinding, not for reading the list |
403 PLACEMENT_SCOPE_MISSING |
A Bitrix24 scope for the placement group: crm for CRM cards and lists, im for chats, task for tasks, contact_center for the Contact Center |
403 PLACEMENT_APP_GRANT_MISSING — the name of the missing right arrives in error.details.requiredScope. An application's rights are fixed at install and cannot change afterwards, so add the right before reinstalling or calling relink-oauth |
A session token alongside the key: some accounts perform the bind with the key alone, others also require the Authorization: Bearer header |
401 SESSION_REQUIRED |
A commercial Bitrix24 plan — the exact condition for the account is returned up front by the placements.bindPrerequisite block in the key data. Infrastructure and key issuance additionally require a Vibe+ plan (INT_VIBE_PLUS_REQUIRED) |
403 INT_TARIFF_REQUIRED |
The conditions above apply to binding and unbinding. Listing bound placements requires only the application key, and the Available placements reference is the exception — it is open to any valid key, and its code list is best read from a live call: it keeps growing, and there is no need to hardcode it. Details on each operation — Placements.
Known specifics
- An application belongs to a single account. The application record is created on the account whose key is passed at creation, and it acts on behalf of users of that same account. To make the same application work on another account, register it there with a separate
POST /v1/appscall using that account's key. A distributable application is one codebase deployed as a separate record on each account. - The scope set is fixed at creation. It cannot be changed for an existing application — to work with a different scope set, recreate the application. Without
placementin the set, publishing returnsMISSING_SCOPE, and a request that needs a scope outside the set returnsBITRIX_ACCESS_DENIED. More on the Scopes page. - Placements with smart processes. Dynamic codes of the form
CRM_DYNAMIC_<entityTypeId>_DETAIL_TABare accepted on par with static ones — the identifier depends on the account. - The exact list of placement codes.
placementsaccepts only codes from a fixed set — the current list is returned by Available placements. Codes outside the set return400 VALIDATION_ERRORon binding. If the code you need is not in/available— it is not supported. appUrlmay contain a path. The value can be a bare subdomain or an address with a path, for examplehttps://app-abc12345.vibecode.bitrix24.com/hh-connector. This is a working technique for several applications behind one Black Hole subdomain: split them by paths via a reverse proxy, and give each application its ownappUrlwith the path you need.- An Open Channels connector needs the
imopenlinesscope. For an application that registers an external-messenger connector for Open Channels, addimopenlinesto the scope set. The full list of available scopes — on the Scopes page.
Windows / PowerShell and UTF-8
Non-ASCII characters (accented letters such as ö, ü, é, or characters from any non-Latin script) in the application title may turn into question marks (?) if the request is sent from Windows PowerShell without explicit UTF-8 serialization. This is not a display problem on the Bitrix24 account side — the bytes are lost before the HTTP request is sent, on the client side.
Cause. By default, PowerShell re-encodes the string from the -Body parameter of Invoke-WebRequest and Invoke-RestMethod into the system's legacy (non-UTF-8) code page — for example windows-1252 on Western installs — and any character outside that page is lost before the request is assembled. The Content-Type: charset=utf-8 header does not help here — by the time it applies, the original bytes are already lost.
Solution. Pass the request body as a UTF-8 byte array.
# 1. Console output encoding — does not affect how the body is encoded
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
# 2. Build the JSON and convert it to a UTF-8 byte array
$body = @{
title = 'Sales dashboard'
scopes = @('crm', 'user', 'placement')
} | ConvertTo-Json -Compress
$bytes = [System.Text.Encoding]::UTF8.GetBytes($body)
# 3. Pass a byte array to -Body (not a string) and set the encoding in Content-Type
Invoke-WebRequest `
-Uri 'https://vibecode.bitrix24.com/v1/apps' `
-Method POST `
-Headers @{
'X-Api-Key' = 'YOUR_API_KEY'
'Content-Type' = 'application/json; charset=utf-8'
} `
-Body $bytes
Common mistakes:
- Saving the
.ps1with a UTF-8 BOM — older PowerShell versions may fail to parse the script itself. - Passing a string to
-Body(-Body $body) instead of a byte array (-Body $bytes) — the string is re-encoded through the system code page. - Relying only on
Content-Type: application/json; charset=utf-8withoutUTF8.GetBytes— this header does not restore the lost bytes, it only declares the body encoding to the server.
The same serialization is needed when updating an application — title changes via PATCH /v1/apps/:id with the same UTF-8 body.
Node.js (fetch) and Python (requests) encode the body in UTF-8 themselves, no extra steps required. The problem is specific to PowerShell.
Error codes
Each endpoint page carries its own error table. Below are the general codes returned by any endpoint in this section.
| Code | HTTP | Description |
|---|---|---|
MISSING_API_KEY |
401 | The X-Api-Key header is missing |
INVALID_API_KEY |
401 | Invalid API key |
RATE_LIMITED |
429 | Request limit exceeded |
BITRIX_UNAVAILABLE |
502 | The Bitrix24 portal is unavailable |
INTERNAL_ERROR |
500 | Internal server error |
The full list of general API errors — Errors.
Endpoint reference
| Method | Path | Description |
|---|---|---|
| GET | /v1/apps | List of applications on the account |
| POST | /v1/apps | Create an application |
| GET | /v1/apps/:id | Application data |
| PATCH | /v1/apps/:id | Update an application |
| DELETE | /v1/apps/:id | Delete an application |
| POST | /v1/apps/:id/publish | Publish to the catalog |
| POST | /v1/apps/:id/unpublish | Unpublish |
| POST | /v1/apps/:id/relink-oauth | Relink the application to the account's OAuth application |
| GET | /v1/placements | Placements bound by the application |
| GET | /v1/placements/available | Available placements reference |
| POST | /v1/placements/bind | Bind a placement |
| POST | /v1/placements/unbind | Unbind a placement |
An interactive method switcher with examples and response fields — Endpoints.