
## Update an application

`PATCH /v1/apps/:id`

Changes application properties. Send only the fields you want to change.

## Parameters

| Parameter | Type | Required | Description |
|----------|-----|:-----:|---------|
| `id` (path) | string | yes | Application identifier from [create](/docs/apps/create) or [list](/docs/apps/list) |

## Request fields (body)

| Field | Type | Description |
|------|-----|---------|
| `title` | string | Name, from 1 to 255 characters, not whitespace only. For an application in the catalog the limit is 100 characters |
| `description` | string \| null | Description, up to 2000 characters |
| `appUrl` | string | Application URL: an address on the platform or a public `https://` address outside it (no `localhost`, private network addresses or credentials). An empty string is stored as `null` |
| `scopes` | array | Set of application scopes, at least one. A change to the platform `vibe:*` scopes is propagated to the application's active keys — see "Known specifics". A Bitrix24 account right cannot be added to an application that already exists, while removing rights succeeds |
| `redirectUris` | array | OAuth redirect URLs |
| `placements` | array | Placements. List of codes — [Available placements](/docs/apps/placements/available). To bind or remove a single placement without republishing the application — [Placements](/docs/apps/placements) |
| `placementResizeEnabled` | boolean | Whether to fit the placement iframe height to the application content. Defaults to `false`. Before turning it on, allow the platform origin in your own `frame-ancestors` directive — the condition is described in [What the app receives](/docs/infra/app-runtime) |

`handlerUrl` cannot be changed — passing the field in the body results in `400 HANDLER_URL_READONLY`.

## Examples

### curl — personal key

```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/dashboard" }'
```

### curl — OAuth application

```bash
curl -X PATCH https://vibecode.bitrix24.com/v1/apps/APP_ID \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "appUrl": "https://app-abc12345.vibecode.bitrix24.com/dashboard" }'
```

### JavaScript — personal key

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/apps/APP_ID', {
  method: 'PATCH',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    appUrl: 'https://app-abc12345.vibecode.bitrix24.com/dashboard',
  }),
})

const { data } = await res.json()
```

### JavaScript — OAuth application

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/apps/APP_ID', {
  method: 'PATCH',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    appUrl: 'https://app-abc12345.vibecode.bitrix24.com/dashboard',
  }),
})

const { data } = await res.json()
```

## Response fields

| Field | Type | Description |
|------|-----|---------|
| `success` | boolean | Always `true` on success |
| `data` | object | Application after the update — the same fields as the [GET /v1/apps/:id](/docs/apps/get) response |
| `data.id` | string | Application identifier |
| `data.title` | string | Name |
| `data.description` | string \| null | Description |
| `data.scopes` | string[] | Set of application scopes |
| `data.handlerUrl` | string | Handler URL, set by the platform. It cannot be changed |
| `data.appUrl` | string \| null | Application URL |
| `data.redirectUris` | string[] | OAuth redirect URLs |
| `data.bitrixClientId` | string \| null | OAuth client identifier in the Bitrix24 account |
| `data.prefix` | string | Application key prefix |
| `data.suffix` | string | Last characters of the application key |
| `data.authorId` | string | Author identifier in the Vibecode platform. To identify the Bitrix24 employee, use `data.authorBitrixUserId` only when `data.authorBitrixUserIdSource: "member"` |
| `data.authorBitrixUserId` | integer \| null | Bitrix24 employee id in the same format as `id` in [GET /v1/users](/docs/entities/users/list). Treat it as the author's confirmed id only when the source is `member`. `null` when the id is unknown |
| `data.authorBitrixUserIdSource` | string \| null | Where the id came from: `member` — the author's confirmed membership of the account. `snapshot` — a value saved when the application was created: it is unconfirmed and may point to a different employee after ownership changes. `null` — no id |
| `data.portalId` | string | Identifier of the Bitrix24 account linked to the key. [GET /v1/me](/docs/keys-auth/me) returns the same field |
| `data.createdAt` | string | Creation date, ISO 8601 |
| `data.updatedAt` | string | Modification date, ISO 8601 |
| `data.placements` | string[] | Saved codes of [placements](/docs/apps/placements) |
| `data.catalogStatus` | string | Catalog status: `PRIVATE` / `PUBLISHED` / `UNPUBLISHED`. The reliable publication signal |
| `data.publishedAt` | string \| null | Publication date, ISO 8601. `null` if the application was never published |
| `data.placementResizeEnabled` | boolean | Whether to adjust the placement iframe height to fit the application content |
| `data.mobile` | boolean | The "Supports BitrixMobile" flag sent to the Bitrix24 account at application registration. This request does not change it — see "Known specifics" |
| `data.hosting` | string | Where the app runs: `platform` — on a platform server linked to the app or owned by this Bitrix24 account, or on that server's verified own domain, or the app has no address at all, `platform-orphan` — an address on a platform subdomain with no live server behind it, `external` — any other address, including a platform server of another Bitrix24 account. Computed on every read |
| `data.externalHost` | string \| null | Host name from `appUrl` (punycode) for `external`, otherwise `null`. An `external` row can also carry `null` when the address has no host: the string does not parse as a URL or its scheme has no host |
| `data.externalRegistrable` | string \| null | Registrable domain of the host by the public suffix list, private suffixes included: `example.co.uk` for `shop.example.co.uk`, the whole address for an IP address. `null` whenever `externalHost` is `null` |
| `warnings` | array | Appears on partial placement sync — the codes that could not be bound, as well as the placements that could not be removed. For removals the entry differs depending on the rollout — see the note under the error table. A separate entry arrives when the new title has lost its non-ASCII characters in transit: it names the field, and the rename itself still goes through. The set of entries is open — show an unfamiliar one as is rather than discarding it |

## Response example

The example shows the main fields. The full set of fields is in the "Response fields" table above.

```json
{
  "success": true,
  "data": {
    "id": "33c4d5e6-f7a8-49b0-1234-5c6d7e8f9012",
    "title": "Sales dashboard",
    "description": "Updated",
    "scopes": ["crm", "user", "placement"],
    "handlerUrl": "https://vibecode.bitrix24.com/v1/bitrix-handler",
    "appUrl": "https://app-abc12345.vibecode.bitrix24.com/dashboard",
    "redirectUris": [
      "https://vibecode.bitrix24.com/oauth/complete",
      "http://localhost"
    ],
    "bitrixClientId": "local.7c3d4e5f6a7b80.55556666",
    "prefix": "vibe_app_local_7c3",
    "suffix": "6666",
    "authorId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "authorBitrixUserId": 42,
    "authorBitrixUserIdSource": "member",
    "portalId": "8b1f0e2a-3c4d-5e6f-7a8b-9c0d1e2f3a4b",
    "createdAt": "2026-06-24T09:12:45.781Z",
    "updatedAt": "2026-06-24T09:13:36.974Z",
    "placements": []
  }
}
```

## Error response example

404 — application not found:

```json
{
  "success": false,
  "error": {
    "code": "APP_NOT_FOUND",
    "message": "Application not found"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|---------|
| 400 | `VALIDATION_ERROR` | Body validation failed: for example a whitespace-only `title`, a title longer than 255 characters, or an invalid `appUrl` |
| 400 | `HANDLER_URL_READONLY` | `handlerUrl` passed in the body — the field is set by the platform and cannot be changed |
| 400 | `NO_USER_TOKEN` | Changing `placements` or renaming a published application without a user token on a cloud portal |
| 400 | `TITLE_TOO_LONG_FOR_CATALOG` | The new name is longer than 100 characters for an application that is in the catalog |
| 400 | `APP_URL_UNSAFE` | An address outside the platform must be a public `https://` address: no `http://`, no `localhost` or private network, no credentials in the URL |
| 403 | `INFRA_DISABLED_FOR_PORTAL` | The request adds `vibe:infra` to `scopes` and the Bitrix24 account administrator has turned server management off. Neither the application nor its keys change. Editing an application that already carries the scope goes through on a closed account too, and removing the scope is always allowed |
| 403 | `INFRA_FORBIDDEN_FOR_COWORK_KEY` | The call was made with a Cowork key — such a key works with data only and cannot perform write operations. To issue a key that can, see [Project key for deploy](/docs/cowork/deploy-key) |
| 403 | `UNAUTHORIZED` | The call was made with an authorization key `vibe_app_…` issued for a different application. Such a key updates only its own application, even when both applications were created by the same author |
| 403 | `UNAUTHORIZED` | The key does not belong to the author or owner of the application |
| 403 | `OAUTH_SCOPE_CHANGE_REQUIRES_REISSUE` | The request adds to `scopes` a Bitrix24 account right the application did not have. The set of account rights is fixed when the application is authorized, so an addition is refused while removing rights succeeds. Declare the right you need when you create the application — [Create an application](/docs/apps/create) |
| 403 | `WRITE_BLOCKED_READONLY_KEY` | The request is made with a key in read-only mode. Such a key cannot change the app — neither `scopes` nor any other field. Details — [Access mode](/docs/keys-auth/access-mode) |
| 403 | `WRITE_BLOCKED_READONLY_KEY` | The key does not change Bitrix24 data, and the app is not the key's own. An app counts as the key's own when this same key created it; for Cowork seat keys, when any key of the same seat created it; for an authorization key, when the key was issued for that app. An app created in the dashboard never counts as such a key's own |
| 403 | `WRITE_BLOCKED_READONLY_KEY` | The key does not change Bitrix24 data, and the change to a published app affects a placement in an employee work screen or placements on a handler other than the platform handler. Affected placements are the added and removed ones when `placements` changes, and all bound ones when the title changes. A change that affects only a left menu item on the platform handler passes without a refusal |
| 403 | `WRITE_BLOCKED_READONLY_KEY` | The key does not change Bitrix24 data, and the request changes `appUrl` or `redirectUris` while the app has a key allowed to write to Bitrix24 in any status, revoked and blocked included, or has no keys at all |
| 403 | `SCOPE_GRANT_REQUIRES_CONSENT` | The request comes from a key with a fixed rights set and adds to `scopes` a platform `vibe:*` scope that the calling key itself does not hold. The list of such scopes arrives in `error.details.unconsented`. Removing scopes always succeeds, adding them is allowed only within the calling key's own rights. The rights set is fixed for a [partner key](/docs/partner-connect), for a [Cowork](/docs/cowork) project key, for a personal key issued by the Vibecode dashboard form and for a key issued with `exactScopes: true` — [Management keys](/docs/management-keys). How to tell such a key apart — [Create an application](/docs/apps/create) |
| 403 | `SCOPE_GRANT_REQUIRES_CONSENT` | The request comes from a [partner key](/docs/partner-connect) and adds `vibe:ai` to `scopes`: a partner key does not pass this scope on to paired keys, even when it holds the scope itself. `error.details.unconsented` carries `["vibe:ai"]` |
| 403 | `EXTERNAL_URL_ACTOR_NOT_ALLOWED` | A published app cannot be switched to an address not served by a live server of this portal on the platform (an external host or a domain whose server is gone) with an app key, a [Connect partner](/docs/partner-connect) key, a server-linked key, a Cowork key, a key with a purpose, a management key, or the working key of an agent or managed bot. Use the Vibecode dashboard or a personal key. The address of an unpublished app is not subject to the key check |
| 403 | `EXTERNAL_URL_DISABLED` | Publishing apps with an address outside the platform is unavailable for this Bitrix24 account |
| 404 | `APP_NOT_FOUND` | No application with the specified `id` |
| 409 | `APP_URL_CONFLICT` | The app address or status changed concurrently. Re-read the app and retry |
| 422 | `APP_HOST_NOT_ALLOWED_HERE` | `appUrl` points at a named address of the platform app domain (`<name>.<domain>`, not `app-…`) whose name is not bound to a server of your Bitrix24 account. Nothing is stored and Bitrix24 is not called. A name of your own server is accepted and stored as that server's `app-…` address |
| 502 | `BITRIX_PARTIAL_REBIND` | Bitrix24 rejected a new placement title during a developer-key rename. The new name is not saved. `error.restored` contains placements returned to the previous title; `error.unbound` contains the placements that need checking in the account: those left removed, plus those whose removal Bitrix24 never confirmed. Rejection diagnostics are in the parallel `error.bitrixCodes` and `error.bitrixStatuses` arrays |
| 502 | `PLACEMENT_UNBIND_FAILED` | Bitrix24 did not confirm the removal of the placements dropped from `placements`. The rest of the request — the remaining fields and the title re-binding — is not applied, and the codes are listed in `error.placements`. The `placements` field itself has already been rewritten and reflects the actual state on the Bitrix24 account, so re-read the application. Arrives only once the removal check is enabled — see the note below |
| 503 | `NETWORK_DEVKEY_REQUIRED` | The Bitrix24 account is switched to developer-key transport and the application author holds no such key. A retry will not help — ask the author to reconnect the account. Arrives only once the removal check is enabled — see the note below |

Full list of common API errors — [Errors](/docs/errors).

**The account-side placement-removal check is currently rolling out per account, so there are two cases:**

- **While the capability is not enabled on the account** — a `PATCH` with a reduced `placements` set answers `200` even when the extra placements could not be removed on the account. The code that was not removed STAYS in the application's `placements`, and the reason arrives as a `Failed to unbind <code>` entry in `warnings`. That entry is the only sign the list has drifted from the account, so for now `warnings` has to be parsed. The codes `PLACEMENT_UNBIND_FAILED` and `NETWORK_DEVKEY_REQUIRED` do not occur on this endpoint.
- **Once enabled** — an unconfirmed removal answers `502 PLACEMENT_UNBIND_FAILED`, and the rest of the request is not applied: neither the title re-binding nor the remaining fields. Retry the request — the placements listed in `error.placements` are still on the account. A `502` here does not mean "nothing changed": the `placements` list has already been rewritten with the removed set, so re-read the application through [Application data](/docs/apps/get) instead of trusting the list you held before the call. A `200` carrying a placement that was not removed does not occur on this path — on a successful response the removal entries in `warnings` are limited to confirmation notes. Enabling it does not change the rest of the set — binding failures and the other reasons listed in the `warnings` row above arrive on this path too.

## Known specifics

- **Renaming an application in the catalog also changes the labels in the Bitrix24 account.** The new `title` goes into the catalog card (the `catalogTitle` field is written together with `title`) and into the placements — the left-menu item and the CRM tabs. The rename therefore calls Bitrix24 and can return `400 NO_USER_TOKEN` or `502 BITRIX_PARTIAL_REBIND`. On a rejection the name is not saved. On BOX and developer-key paths, after a confirmed removal and a rejected new binding, the platform makes one attempt to restore the previous title. Successfully restored placements are listed in `error.restored`; they need no manual repair, but the rename is still refused. Only placements in `error.unbound` need checking: they were either left removed, or Bitrix24 never confirmed that the previous binding was removed. Restore them through [`POST /v1/placements/bind`](/docs/apps/placements/bind), then repeat the PATCH. The paired `error.bitrixCodes` and `error.bitrixStatuses` arrays contain the code and HTTP status for every rejected call, new title first and previous title second; missing values become `B24_ERROR` and `0`, while raw rejection text and the developer-key URL are never returned. The cloud OAuth rebind does not compensate, but it refuses too when Bitrix24 confirmed neither the removal of the previous binding nor the new one: such a placement lands in `error.unbound`, the response is 502, and the name is not saved. Bitrix24 caches the left menu — the new name shows up after a hard page reload. For an application outside the catalog the rename changes `title` only.
- **Changing `scopes` propagates the platform `vibe:*` scopes into the application's keys.** The update compares the old and the new set and applies the `vibe:*` difference to every active key of the application — an added `vibe:ai` appears on the keys, a removed `vibe:infra` disappears from them. Bitrix24 account scopes (`crm`, `user` and the rest) are not propagated: their set is fixed when the key is issued and changes only by reissuing it.
- **A key with a fixed rights set receives no platform scopes through propagation.** That set is fixed on the paired key of an application created with a [partner key](/docs/partner-connect), with a [Cowork](/docs/cowork) project key, with a personal key from the Vibecode dashboard form or with a key issued using `exactScopes: true` — [Create an application](/docs/apps/create). Removals reach it like any other key, additions do not: a `vibe:infra` added to the application's `scopes` will not appear on such a key, and [`POST /v1/infra/servers`](/docs/infra/servers/create) keeps answering `403 INFRA_SCOPE_REQUIRED`. The required right is declared in `scopes` when the application is created.
- **A mismatch between the application scopes and its key is fixed with two requests.** The application's `scopes` can carry `vibe:infra` while its key does not — the key returns `403` when creating a server. Remove `vibe:infra` from `scopes` with the first request and put it back with the second: propagation applies exactly the difference, so the second edit adds the scope to the key. This has no effect on a key with a fixed rights set — see the paragraph above. The key's effective set is shown by [`GET /v1/me`](/docs/keys-auth/me) in `data.scopes` when called with that key.
- **Changing `placements` of a published application triggers placement sync in the Bitrix24 account.** Binding and unbinding happen directly in the Bitrix24 account, and some codes may fail to bind — then the response carries `warnings` with a list of such codes. On a cloud account without a user token the sync is impossible, and the request returns `400 NO_USER_TOKEN`. For an application in `PRIVATE` status, `placements` are written to the database without contacting Bitrix24.
- **An `appUrl` edit reaches the application card in the Bitrix24 catalog on its own.** The card is queued for an update by this same request — no separate call is needed. The card opens the full address of the application — subpath, query parameters and fragment included — when that address points to the same Black Hole subdomain as the card's server. In every other case the card opens the server root — including when the address uses a different subdomain, a custom domain or a different scheme, is empty or unparsable, carries a username before the host, or is longer than 512 characters, a limit the query parameters count toward. A separate gateway limitation applies regardless of the card: on the first visit within a session the visitor lands on the subdomain root whatever the card address is — [App deployment](/docs/infra/deploy/deploy).
- **Non-ASCII characters in `title` from Windows PowerShell.** Sent without explicit UTF-8 serialization, they are stored as question marks (`?`): the bytes are lost on the client side, before the request is sent. A ready-to-run call with `UTF8.GetBytes` — [Windows / PowerShell and UTF-8](/docs/apps#windows-powershell-and-utf-8).
- **The `mobile` field is not editable.** A value sent in the body is ignored, and the response is `200` with no change to the record. Bitrix24 has no method for updating a local application, so the flag is set only at [creation](/docs/apps/create). For an existing application, change the flag in the application card on the Bitrix24 account, or create the application again.

## See also

- [Applications](/docs/apps)
- [Create an application](/docs/apps/create)
- [Application data](/docs/apps/get)
- [Delete an application](/docs/apps/delete)
- [Publish](/docs/apps/publish)
