## Bound placements

`GET /v1/placements`

Returns the placements that are listed as bound for the application. When a session token is passed together with the application key, Vibecode reconciles this list with the Bitrix24 account and reports the outcome of that reconciliation in a dedicated field — matched, diverged, or not checked.

Call preconditions — [what is required before binding](/docs/apps#placements).

## Examples

A personal key `vibe_api_…` receives `400 OAUTH_APP_REQUIRED`. Use an OAuth application key `vibe_app_…`.

### curl — OAuth application

```bash
curl https://vibecode.bitrix24.com/v1/placements \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN"
```

### JavaScript — OAuth application

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/placements', {
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
  },
})
const { data } = await res.json()
console.log('Confirmed by the account:', data.handlers)
```

## Response fields

| Field | Type | Description |
|------|-----|---------|
| `success` | boolean | Always `true` on success |
| `data` | object | The application's saved placements and the result of checking them against the account |
| `data.placements` | string[] | Placement codes bound by the application |
| `data.appId` | string | Application identifier. List of applications — [GET /v1/apps](/docs/apps/list) |
| `data.appTitle` | string | Application name |
| `data.handlers` | object[] | Handler data received from the account. The field is absent if the account response was not read. An empty array means the response was received but contained none of the bound codes. Read the reconciliation outcome from `data.portalSync`, not from the presence or the length of this array |
| `data.handlers[].placement` | string | Placement code |
| `data.handlers[].handler` | string | Handler address registered on the account |
| `data.handlers[].misbound` | boolean | `true` when the handler is registered to the technical address of the application server. Such a placement will not pass authorization inside Bitrix24 |
| `data.handlers[].title` | string | Placement caption. Returned if the account provided it as a string |
| `data.handlers[].options` | array \| object | Placement settings, if returned by the account. An empty array means there are no settings |
| `data.handlers[].langAll` | object \| array | Placement captions by language, if returned by the account. For an object, each key is a language code and its value is an object with `TITLE`, `DESCRIPTION`, `GROUP_NAME` fields |
| `data.portalSync` | string | Reconciliation outcome against the account. `ok` — the account returned every bound code, `drift` — the account did not return some of the codes, `unknown` — no reconciliation was performed. Returned in every successful response |
| `data.portalSyncReason` | string | Why `portalSync` is not `ok`. `no_oauth_session` — no session token was passed, `empty_vibe_list` — the application has no bound placements, `b24_unreachable` — the account response could not be obtained, `unpublished` — the application is withdrawn from the catalog, removed placements are not treated as a divergence, `missing_on_portal` — the account did not return some of the codes. Absent when `ok` |
| `data.missingOnPortal` | string[] | Codes that are listed as bound in Vibecode and were not returned by the account. Returned only when `portalSync` equals `drift` |
| `warnings` | string[] | Appears when at least one placement has `misbound` equal to `true` or when `portalSync` equals `drift` |

## Response example

A session token was passed and the account returned both bound codes — `portalSync` equals `ok`:

```json
{
  "success": true,
  "data": {
    "placements": ["LEFT_MENU", "CRM_DEAL_DETAIL_TAB"],
    "appId": "3d5f7a91-2b4c-4e8f-9a01-6c7d8e9f0a1b",
    "appTitle": "Sales dashboard",
    "handlers": [
      {
        "placement": "CRM_DEAL_DETAIL_TAB",
        "handler": "https://example.com/tab",
        "misbound": false,
        "title": "Deal documents",
        "options": [],
        "langAll": {
          "en": { "TITLE": "Deal documents", "DESCRIPTION": "", "GROUP_NAME": "" },
          "ru": { "TITLE": "Deal documents", "DESCRIPTION": "", "GROUP_NAME": "" }
        }
      },
      {
        "placement": "LEFT_MENU",
        "handler": "https://example.com/menu",
        "misbound": false,
        "title": "Sales dashboard",
        "options": [],
        "langAll": {}
      }
    ],
    "portalSync": "ok"
  }
}
```

The account did not return a bound code — this is a divergence, and the code is listed in `missingOnPortal`:

```json
{
  "success": true,
  "data": {
    "placements": ["LEFT_MENU"],
    "appId": "3d5f7a91-2b4c-4e8f-9a01-6c7d8e9f0a1b",
    "appTitle": "Sales dashboard",
    "handlers": [],
    "portalSync": "drift",
    "portalSyncReason": "missing_on_portal",
    "missingOnPortal": ["LEFT_MENU"]
  },
  "warnings": [
    "Placements LEFT_MENU are listed as bound but were not returned by the Bitrix24 account; re-bind via POST /v1/placements/bind to restore them on the portal."
  ]
}
```

No session token was passed — no reconciliation happened and `handlers` is absent:

```json
{
  "success": true,
  "data": {
    "placements": ["LEFT_MENU"],
    "appId": "3d5f7a91-2b4c-4e8f-9a01-6c7d8e9f0a1b",
    "appTitle": "Sales dashboard",
    "portalSync": "unknown",
    "portalSyncReason": "no_oauth_session"
  }
}
```

## Error response example

400 — the request was made with a personal key:

```json
{
  "success": false,
  "error": {
    "code": "OAUTH_APP_REQUIRED",
    "message": "Placement management is only available for OAuth app keys"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|---------|
| 400 | `OAUTH_APP_REQUIRED` | The request was made with a personal `vibe_api_` key |
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header was not passed |
| 401 | `INVALID_API_KEY` | The key is not recognized — no such key exists on the platform |
| 404 | `APP_NOT_FOUND` | No application is linked to the key |

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

## Known specifics

- **`unknown` is not equivalent to `ok`.** This value means the binding is unconfirmed and can occur in two cases. If the account response was not read, the reason is `no_oauth_session`, `empty_vibe_list`, or `b24_unreachable`, and the `handlers` field is absent from the response. If the account response was read but its state is not compared with our list, the reason is `unpublished`, and `handlers` is present.
- **An application withdrawn from the catalog is not treated as a divergence.** With `unpublished` the verdict is suppressed: removed placements are the expected consequence of the operation. The account is still queried, because [unpublishing](/docs/apps/unpublish) does not guarantee that every placement was actually removed, so `handlers` and the `misbound` flag stay in the response. What remains in `placements` after a withdrawal, and how to remove the leftovers, is covered on the operation's own page.
- **A divergence is resolved by binding again.** The codes from `missingOnPortal` are returned to the account by calling [Bind a placement](/docs/apps/placements/bind).
- **Codes that are absent from `data.placements` are not treated as a divergence.** The account may hold placements of other applications, and that does not affect `portalSync`.
- **A placement with `misbound` equal to `true` is a separate state, not a divergence.** The account did return such a placement, so it never lands in `missingOnPortal` and `portalSync` stays `ok` — or `unknown` with the `unpublished` reason when the application is withdrawn from the catalog. A call to [Bind a placement](/docs/apps/placements/bind) replaces the technical address of the application server with the platform handler address.

## See also

- [Placements](/docs/apps/placements)
- [Available placements](/docs/apps/placements/available)
- [Bind a placement](/docs/apps/placements/bind)
- [Unbind a placement](/docs/apps/placements/unbind)
- [Applications](/docs/apps)
- [Key data](/docs/keys-auth/me)
