
# 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](#which-key-to-choose) | [Application states](#application-states) | [Full cycle](#full-cycle) | [Placements](#placements) | [Known specifics](#known-specifics) | [Windows / PowerShell and UTF-8](#windows-powershell-and-utf-8) | [Error codes](#error-codes) | [Endpoint reference](#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](/docs/keys-auth#how-many-keys-you-can-create).

A detailed description of key types, formats, and obtaining `session_token` — [Keys and authorization](/docs/keys-auth).

---

## 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](/docs/apps/get) 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:

- `appUrl` is empty until you set it via [updating the application](/docs/apps/update) or pass it in the publish body.
- `placements` is empty because placements are bound to the account only at the moment of publishing.
- `catalogStatus` is `PRIVATE` and `publishedAt` is `null` until the application is published.
- The application appears in the catalog only after [publishing](/docs/apps/publish).

### `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](/docs/apps/create) 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.

```bash
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](/docs/infra/deploy/deploy)) and make sure `appUrl` points to its address. If the address was not set at creation — set it via [updating](/docs/apps/update):

```bash
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](/docs/apps/publish) 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`.

```bash
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](/docs/apps/placements) 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](/docs/keys-auth/me) | `403 INT_TARIFF_REQUIRED` |

The conditions above apply to binding, unbinding, and listing bound placements. The [Available placements](/docs/apps/placements/available) 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](/docs/apps/placements).

---

## Known specifics

- **An application belongs to a single account.** The application record is created on the account whose key is passed at [creation](/docs/apps/create), 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/apps`](/docs/apps/create) call 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 `placement` in the set, publishing returns `MISSING_SCOPE`, and a request that needs a scope outside the set returns `BITRIX_ACCESS_DENIED`. More on the [Scopes](/docs/scopes) page.
- **Placements with smart processes.** Dynamic codes of the form `CRM_DYNAMIC_<entityTypeId>_DETAIL_TAB` are accepted on par with static ones — the identifier depends on the account.
- **The exact list of placement codes.** `placements` accepts only codes from a fixed set — the current list is returned by [Available placements](/docs/apps/placements/available). Codes outside the set return `400 VALIDATION_ERROR` on binding. If the code you need is not in `/available` — it is not supported.
- **`appUrl` may contain a path.** The value can be a bare subdomain or an address with a path, for example `https://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 own `appUrl` with the needed path.
- **An Open Channels connector needs the `imopenlines` scope.** For an application that registers an external-messenger connector for Open Channels, add `imopenlines` to the scope set. The full list of available scopes — on the [Scopes](/docs/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.

```powershell
# 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 `.ps1` with 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-8` without `UTF8.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](/docs/apps/update) — `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](/docs/errors).

---

## Endpoint reference

| Method | Path | Description |
|-------|------|----------|
| GET | [/v1/apps](/docs/apps/list) | List of applications on the account |
| POST | [/v1/apps](/docs/apps/create) | Create an application |
| GET | [/v1/apps/:id](/docs/apps/get) | Application data |
| PATCH | [/v1/apps/:id](/docs/apps/update) | Update an application |
| DELETE | [/v1/apps/:id](/docs/apps/delete) | Delete an application |
| POST | [/v1/apps/:id/publish](/docs/apps/publish) | Publish to the catalog |
| POST | [/v1/apps/:id/unpublish](/docs/apps/unpublish) | Unpublish |
| GET | [/v1/placements](/docs/apps/placements/list) | Placements bound by the application |
| GET | [/v1/placements/available](/docs/apps/placements/available) | Reference of available placements |
| POST | [/v1/placements/bind](/docs/apps/placements/bind) | Bind a placement |
| POST | [/v1/placements/unbind](/docs/apps/placements/unbind) | Unbind a placement |

An interactive method switcher with examples and response fields — [Endpoints](/docs/apps/endpoints).

## See also

- [Placements](/docs/apps/placements) — binding the application to points in the Bitrix24 interface
- [Endpoints](/docs/apps/endpoints) — all section methods in tabs
- [Creating a key](/docs/keys-auth) — issuing an API key and an OAuth application
- [Scopes](/docs/scopes) — which scopes a task needs
- [Deploy API](/docs/infra/deploy/deploy) — deploy the application code
- [Source storage](/docs/source-storage) — source snapshots and the check at publishing
- [Event subscriptions](/docs/infra/event-subscriptions) — delivering Bitrix24 events to the app server
