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 Bitrix24 account catalog 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 | 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 limit is raised by the account administrator. 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 placements 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"] }'
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 manage them one at a time and change the label and the handler address without republishing.
A few conditions have to be met before the first bind — each unmet one 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 |
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 |
502 BITRIX_UNAVAILABLE — the account answers that the placement was not found |
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 |
403 INT_TARIFF_REQUIRED |
The conditions above apply to binding, unbinding, and listing bound placements. 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 needed path.- 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 (accents such as ö, ü, é, or 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 of the 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 |
| GET | /v1/placements | Placements bound by the application |
| GET | /v1/placements/available | Reference of available placements |
| POST | /v1/placements/bind | Bind a placement |
| POST | /v1/placements/unbind | Unbind a placement |
An interactive method switcher with examples and response fields — Endpoints.
See also
- Placements — binding the application to points in the Bitrix24 interface
- Endpoints — all section methods in tabs
- Creating a key — issuing an API key and an OAuth application
- Scopes — which scopes a task needs
- Deploy API — deploy the application code
- Source storage — source snapshots and the check at publishing
- Event subscriptions — delivering Bitrix24 events to the app server