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

`POST /v1/ai/credentials`

Подключает новый ключ провайдера к вашему пользователю (USER scope). Ключ автоматически проверяется у провайдера до сохранения: непрошедший проверку ключ не сохраняется. Один пользователь может подключить только один ключ на каждого провайдера.

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

| Поле | Тип | Обяз. | По умолч. | Описание |
|------|-----|:-----:|-----------|----------|
| `providerId` | string | да | — | ID провайдера. Список: [`GET /v1/ai/providers`](/docs/ai/credentials/providers) |
| `name` | string | да | — | Произвольное имя ключа для отображения в списке |
| `credentials.apiKey` | string | да | — | Ключ от провайдера (например, `sk-...` для OpenAI) |
| `credentials.baseUrl` | string | условно | — | Обязательно для `custom-openai-compat`. Формат: `https://api.example.com/v1`. Только `http`/`https`, приватные сети запрещены |
| `isDefault` | boolean | нет | `false` | Использовать этот ключ по умолчанию для провайдера |

## Примеры

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

```bash
curl -X POST https://vibecode.bitrix24.tech/v1/ai/credentials \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "providerId": "openai",
    "name": "Мой личный OpenAI",
    "credentials": {
      "apiKey": "sk-proj-..."
    },
    "isDefault": true
  }'
```

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

```bash
curl -X POST https://vibecode.bitrix24.tech/v1/ai/credentials \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "providerId": "openai",
    "name": "Мой личный OpenAI",
    "credentials": {
      "apiKey": "sk-proj-..."
    },
    "isDefault": true
  }'
```

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

```javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/ai/credentials', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    providerId: 'openai',
    name: 'Мой личный OpenAI',
    credentials: { apiKey: 'sk-proj-...' },
    isDefault: true,
  }),
})

const { success, data } = await res.json()
if (success) console.log('Ключ подключён, ID:', data.id)
```

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

```javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/ai/credentials', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    providerId: 'openai',
    name: 'Мой личный OpenAI',
    credentials: { apiKey: 'sk-proj-...' },
  }),
})

const { data } = await res.json()
console.log('ID:', data.id)
```

## Подключение Custom OpenAI-Compatible сервиса

Для произвольного OpenAI-совместимого сервиса (Minimax, Cerebras, Cohere, корпоративный `vLLM`/`Ollama` и т. п.) используйте `providerId: cprv_custom_seed` и обязательно укажите `baseUrl`:

```bash
curl -X POST https://vibecode.bitrix24.tech/v1/ai/credentials \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "providerId": "cprv_custom_seed",
    "name": "Корпоративный сервис",
    "credentials": {
      "apiKey": "sk-corp-...",
      "baseUrl": "https://llm.example.com/v1"
    }
  }'
```

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

| Поле | Тип | Описание |
|------|-----|----------|
| `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.createdAt` | string | Время создания в `ISO 8601` |
| `data.updatedAt` | string | Время последнего обновления в `ISO 8601` |

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

HTTP-статус `201 Created`:

```json
{
  "success": true,
  "data": {
    "id": "cred_abc123def456",
    "providerId": "openai",
    "provider": {
      "slug": "openai",
      "name": "OpenAI"
    },
    "name": "Мой личный OpenAI",
    "isDefault": true,
    "createdAt": "2026-04-27T11:30:00.000Z",
    "updatedAt": "2026-04-27T11:30: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` указывает на приватную сеть или не разрешается в IP-адрес через `DNS` |
| 404 | `provider_not_found` | Провайдер с таким `providerId` не существует или отключён |
| 409 | `already_exists` | Ключ для этого провайдера уже подключён — используйте [`PATCH /v1/ai/credentials/:id`](./update.md) |
| 422 | `credential_invalid` | Ключ не прошёл верификацию у провайдера. Сообщение содержит детали от провайдера |
| 422 | `provider_geoblocked` | Провайдер отклонил проверку ключа по региону нашего сервера. Подключите свой прокси или перейдите на OpenRouter. Ответ несёт поле `suggestions` |
| 403 | `scope_missing` | API-ключу не хватает скоупа `vibe:ai` |

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

Лимит создания ключей: 10 запросов в минуту (единый бакет на портал — все ключи портала делят его).

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

**Верификация перед сохранением.** Ключ проверяется у провайдера через `verify()` — это HTTP-запрос к `GET /v1/models` или (если провайдер не поддерживает `models`) к `POST /v1/chat/completions` с минимальным телом. Тайм-аут проверки — 10 секунд. При некорректном ключе ничего не сохраняется в базе.

**SSRF-защита для Custom-провайдера.** `baseUrl` для `custom-openai-compat` обязательно проходит проверку: запрещены приватные сети (`127.0.0.0/8`, `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `169.254.0.0/16`), `IPv6` link-local и ULA, `IPv4`-mapped `IPv6`. Принимаются только `http://`/`https://` схемы.

**Сообщение об ошибке очищено.** Текст ошибки от провайдера обрезается до 200 символов, URL заменяются на плейсхолдер `[URL]` — это защищает от случайной утечки внутренних адресов в логах клиента.

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

- [Список провайдеров](/docs/ai/credentials/providers)
- [Обновить ключ](./update.md)
- [Проверить ключ](./test.md)
- [Свои ключи (BYOK)](/docs/ai/credentials)
