
## Обновить ключ провайдера

`PATCH /v1/ai/credentials/:id`

Обновляет имя, флаг `isDefault` и/или сам ключ. Если в запросе передано поле `credentials` — новый ключ перепроверяется у провайдера до сохранения. Непрошедший проверку ключ не сохраняется, в базе ничего не меняется. Поля `name` и `isDefault` обновляются без верификации.

## Параметры

| Параметр | Тип | Обяз. | Описание |
|----------|-----|:-----:|----------|
| `id` (path) | string | да | ID ключа из [`GET /v1/ai/credentials`](./list.md) |

## Поля запроса (body)

| Поле | Тип | Обяз. | Описание |
|------|-----|:-----:|----------|
| `name` | string | нет | Новое имя ключа |
| `isDefault` | boolean | нет | Сделать ключ ключом по умолчанию для провайдера |
| `credentials.apiKey` | string | нет | Новый ключ от провайдера |
| `credentials.baseUrl` | string | нет | Новый `baseUrl` (только для `custom-openai-compat`) |

В `body` нужно передать хотя бы одно поле. Если передано `credentials` — оно проверяется у провайдера; при успехе обнуляются `lastError` и `lastErrorAt`.

## Примеры

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

```bash
curl -X PATCH https://vibecode.bitrix24.tech/v1/ai/credentials/cred_abc123def456 \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "OpenAI (новое имя)",
    "credentials": {
      "apiKey": "sk-proj-new-key-..."
    }
  }'
```

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

```bash
curl -X PATCH https://vibecode.bitrix24.tech/v1/ai/credentials/cred_abc123def456 \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "OpenAI (новое имя)"
  }'
```

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

```javascript
const id = 'cred_abc123def456'
const res = await fetch(`https://vibecode.bitrix24.tech/v1/ai/credentials/${id}`, {
  method: 'PATCH',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    credentials: { apiKey: 'sk-proj-new-key-...' },
  }),
})

const { success, data } = await res.json()
if (success) console.log('Ключ обновлён в', data.updatedAt)
```

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

```javascript
const id = 'cred_abc123def456'
const res = await fetch(`https://vibecode.bitrix24.tech/v1/ai/credentials/${id}`, {
  method: 'PATCH',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ name: 'OpenAI (production)' }),
})

const { data } = await res.json()
console.log('Новое имя:', data.name)
```

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

| Поле | Тип | Описание |
|------|-----|----------|
| `success` | boolean | Всегда `true` при успехе |
| `data.id` | string | ID ключа |
| `data.providerId` | string | ID провайдера |
| `data.provider.slug` | string | Системное имя провайдера |
| `data.provider.name` | string | Название провайдера |
| `data.name` | string | Текущее имя ключа |
| `data.isDefault` | boolean | Флаг ключа по умолчанию |
| `data.lastError` | string \| null | Сбрасывается в `null` при успешном обновлении ключа |
| `data.lastErrorAt` | string \| null | Сбрасывается в `null` при успешном обновлении ключа |
| `data.createdAt` | string | Время создания в `ISO 8601` |
| `data.updatedAt` | string | Время последнего обновления в `ISO 8601` |

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

```json
{
  "success": true,
  "data": {
    "id": "cred_abc123def456",
    "providerId": "openai",
    "provider": {
      "slug": "openai",
      "name": "OpenAI"
    },
    "name": "OpenAI (новое имя)",
    "isDefault": true,
    "lastError": null,
    "lastErrorAt": null,
    "createdAt": "2026-04-15T10:00:00.000Z",
    "updatedAt": "2026-04-27T11:35:00.000Z"
  }
}
```

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

`422 credential_invalid` — новый ключ не прошёл верификацию у провайдера:

```json
{
  "success": false,
  "error": {
    "code": "credential_invalid",
    "message": "OpenAI API 401: Incorrect API key provided"
  }
}
```

## Ошибки

| HTTP | Код | Описание |
|------|-----|----------|
| 400 | `invalid_request` | Нарушена схема `body` |
| 400 | `no_key` | В `credentials` нет поля `apiKey` |
| 400 | `base_url_invalid` | Новый `baseUrl` использует не `http`/`https` |
| 400 | `base_url_private` | Новый `baseUrl` указывает на приватную сеть |
| 404 | `not_found` | Ключ с таким `id` не найден или не принадлежит вам |
| 404 | `provider_not_found` | Провайдер ключа удалён |
| 422 | `credential_invalid` | Новый ключ не прошёл верификацию у провайдера |
| 422 | `provider_geoblocked` | Провайдер отклонил проверку ключа по региону нашего сервера. Подключите свой прокси или перейдите на OpenRouter. Ответ несёт поле `suggestions` |
| 403 | `scope_missing` | API-ключу не хватает скоупа `vibe:ai` |

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

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

**Верификация только при изменении `credentials`.** Если в запросе нет поля `credentials` (обновляются только `name` или `isDefault`) — провайдер не вызывается, поля `lastError`/`lastErrorAt` не сбрасываются.

**Пустое тело запроса допустимо.** `PATCH` с пустым `body` (`{}`) вернёт текущее состояние ключа без изменений — поведение, унаследованное от Zod-схемы. Используйте для проверки доступности ключа без затрагивания данных.

**Доступ только к своим ключам.** Эндпоинт работает только с USER-ключами текущего пользователя. Попытка обновить чужой ключ или PORTAL-ключ вернёт `404`.

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

- [Список ключей](./list.md)
- [Подключить ключ](./create.md)
- [Удалить ключ](./delete.md)
- [Проверить ключ](./test.md)
