
## Снять с публикации

`POST /v1/apps/:id/unpublish`

Переводит опубликованное приложение в `UNPUBLISHED` и отвязывает [места встраивания](/docs/apps/placements) от портала.

## Параметры

| Параметр | Тип | Обяз. | Описание |
|----------|-----|:-----:|---------|
| `id` (path) | string | да | Идентификатор приложения в статусе `PUBLISHED`. Список: `GET /v1/apps` |

Тело запроса не требуется — пустое тело допустимо.

## Примеры

### curl — личный ключ

```bash
curl -X POST "https://vibecode.bitrix24.tech/v1/apps/YOUR_APP_ID/unpublish" \
  -H "X-Api-Key: YOUR_API_KEY"
```

### curl — OAuth-приложение

```bash
curl -X POST "https://vibecode.bitrix24.tech/v1/apps/YOUR_APP_ID/unpublish" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN"
```

### JavaScript — личный ключ

```javascript
const res = await fetch(
  'https://vibecode.bitrix24.tech/v1/apps/YOUR_APP_ID/unpublish',
  {
    method: 'POST',
    headers: { 'X-Api-Key': 'YOUR_API_KEY' },
  }
)
const body = await res.json()
if (!body.success) throw new Error(body.error.code)
console.log(body.data.placements) // []
```

### JavaScript — OAuth-приложение

```javascript
const res = await fetch(
  'https://vibecode.bitrix24.tech/v1/apps/YOUR_APP_ID/unpublish',
  {
    method: 'POST',
    headers: {
      'X-Api-Key': 'YOUR_APP_KEY',
      'Authorization': 'Bearer USER_SESSION_TOKEN',
    },
  }
)
```

## Поля ответа

| Поле | Тип | Описание |
|------|-----|----------|
| `success` | boolean | `true` при успешном снятии с публикации |
| `data` | object | Приложение после снятия — те же 17 полей, что отдаёт [данные приложения](/docs/apps/get) |
| `data.placements` | string[] | Места встраивания, снятие которых не подтверждено. Пустой массив означает, что снято всё. Наполнение зависит от раскатки — см. врезку ниже |
| `data.catalogStatus` | string | После снятия — `UNPUBLISHED` |
| `data.publishedAt` | string \| null | Дата последней публикации сохраняется (снятие её не сбрасывает) |
| `warnings` | string[] | Пояснения по ходу снятия. Поле лежит рядом с `data`, а не внутри него. Строка приходит не только на неудаче: снятие, подтверждённое единственным доступным плечом, тоже печатает свою строку. Набор строк открытый — не выводите исход из наличия поля, читайте `placements` |

**Проверка снятия на стороне аккаунта сейчас раскатывается по аккаунтам, поэтому вариантов два:**

- **Пока возможность не включена на аккаунте** — снятие идёт без ожидания результата по каждому коду, `placements` всегда приходит пустым массивом, а `warnings` не приходит вовсе. Пустой список здесь означает «мы отправили отвязку», а не «аккаунт подтвердил снятие».
- **После включения** — платформа дожидается ответа аккаунта. В `placements` остаются коды, снять которые не удалось, а в `warnings` едут пояснения. Определяйте успех по пустоте `placements`, а не по статусу `200` и не по наличию `warnings`: статус приходит `200` в обоих случаях, потому что снятие с публикации не отказывает никогда.

**Непустой `warnings` сам по себе не означает отказ.** Строки бывают четырёх сортов, и два из них описывают успех: снятие подтверждено единственным доступным плечом, снятие прошло при отказе одного из двух плеч, код снять не удалось, снимать было нечем. На аккаунте с одним доступным плечом — например, на облачном аккаунте без ключа разработчика — своя строка приходит на КАЖДОЕ штатно снятое место. Разбирать их в коде не нужно, они для человека.

## Пример ответа

Поле `data` повторяет объект приложения с пустым массивом `placements`:

```json
{
  "success": true,
  "data": {
    "id": "33c4d5e6-f7a8-49b0-1234-5c6d7e8f9012",
    "title": "Дашборд продаж",
    "description": null,
    "scopes": ["crm", "user", "placement"],
    "handlerUrl": "https://vibecode.bitrix24.tech/v1/bitrix-handler",
    "appUrl": "https://app-abc12345.vibecode.bitrix24.tech",
    "redirectUris": [
      "https://vibecode.bitrix24.tech/oauth/complete",
      "http://localhost"
    ],
    "bitrixClientId": "local.7c3d4e5f6a7b80.55556666",
    "prefix": "vibe_app_local_7c3",
    "suffix": "6666",
    "authorId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "portalId": "8b1f0e2a-3c4d-5e6f-7a8b-9c0d1e2f3a4b",
    "createdAt": "2026-06-24T09:12:45.781Z",
    "updatedAt": "2026-06-24T12:47:05.930Z",
    "placements": [],
    "catalogStatus": "UNPUBLISHED",
    "publishedAt": "2026-06-24T10:03:18.204Z"
  }
}
```

## Пример ответа при ошибке

404 — приложение не опубликовано:

```json
{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "Published app not found"
  }
}
```

## Ошибки

| HTTP | Код | Описание |
|------|-----|----------|
| 403 | `INFRA_FORBIDDEN_FOR_COWORK_KEY` | Вызов сделан ключом Cowork/Code — такой ключ работает только с данными, изменяющие операции ему закрыты. Что делать — [Проектный ключ для деплоя](/docs/cowork/deploy-key) |
| 404 | `NOT_FOUND` | Приложение не существует, принадлежит другому порталу или не находится в статусе `PUBLISHED` |
| 403 | `FORBIDDEN` | Запрос не от автора приложения и не от администратора портала |

Полный список общих ошибок API — [Ошибки](/docs/errors).

## Известные особенности

- **Возврат в `UNPUBLISHED`, а не в `PRIVATE`.** Метаданные каталога — название, описание, иконка — сохраняются, поэтому повторная [публикация](/docs/apps/publish) с пустым телом восстанавливает приложение без повторного заполнения.
- **Отвязка мест встраивания не блокирует снятие ни при какой раскатке.** Сбой отвязки отдельного места не прерывает операцию: приложение переходит в `UNPUBLISHED` всегда. Отказать здесь нельзя по построению — на облачном аккаунте без ключа разработчика плечо снятия одно, отказ на нём запер бы приложение в `PUBLISHED` без обратного пути. Меняется только то, узнаете ли вы о неснятых местах: после включения проверки они приходят в `placements`, до включения список всегда пуст.

## Смотрите также

- [Приложения](/docs/apps)
- [Опубликовать](/docs/apps/publish)
- [Данные приложения](/docs/apps/get)
- [Удалить приложение](/docs/apps/delete)
