
## Test a provider key

`POST /v1/ai/credentials/:id/test`

Re-verifies a saved key with the provider. It changes nothing in the credentials themselves, but updates the key's `lastError` and `lastErrorAt` fields depending on the result. Use it when you suspect the key has expired or the provider changed its API.

## Parameters

| Parameter | Type | Required | Description |
|----------|-----|:-----:|----------|
| `id` (path) | string | yes | Key ID from [`GET /v1/ai/credentials`](./list.md) |

## Examples

### curl — personal key

```bash
curl -X POST https://vibecode.bitrix24.com/v1/ai/credentials/cred_abc123def456/test \
  -H "X-Api-Key: YOUR_API_KEY"
```

### curl — OAuth application

```bash
curl -X POST https://vibecode.bitrix24.com/v1/ai/credentials/cred_abc123def456/test \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN"
```

### JavaScript — personal key

```javascript
const id = 'cred_abc123def456'
const res = await fetch(`https://vibecode.bitrix24.com/v1/ai/credentials/${id}/test`, {
  method: 'POST',
  headers: { 'X-Api-Key': 'YOUR_API_KEY' },
})

const { data } = await res.json()
console.log(data.valid ? 'Key is valid' : `Error: ${data.error}`)
```

### JavaScript — OAuth application

```javascript
const id = 'cred_abc123def456'
const res = await fetch(`https://vibecode.bitrix24.com/v1/ai/credentials/${id}/test`, {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
  },
})

const { data } = await res.json()
if (!data.valid) alert(`Key is invalid: ${data.error}`)
```

## Response fields

| Field | Type | Description |
|------|-----|----------|
| `success` | boolean | Always `true` (even for an invalid key — that is not an endpoint error) |
| `data.valid` | boolean | `true` — the key was accepted by the provider; `false` — rejected |
| `data.error` | string | The error message from the provider when `valid: false`. URLs are replaced with `[URL]`, length truncated to 200 characters |

## Response example

Key is valid:

```json
{
  "success": true,
  "data": {
    "valid": true
  }
}
```

Key was rejected by the provider:

```json
{
  "success": true,
  "data": {
    "valid": false,
    "error": "OpenAI API 401: Incorrect API key provided"
  }
}
```

## Error response example

`404 not_found` — the key was not found or belongs to another user:

```json
{
  "success": false,
  "error": {
    "code": "not_found",
    "message": "Credential not found"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|----------|
| 404 | `not_found` | A key with this `id` was not found or does not belong to you |
| 400 | `no_key` | The saved credentials have no `apiKey` field (a broken old key) |
| 500 | `decryption_error` | Failed to decrypt the saved credentials |
| 422 | `provider_geoblocked` | The provider rejected the key check based on our server's region. Connect your own proxy or switch to OpenRouter. The response carries a `suggestions` field |
| 403 | `scope_missing` | The API key lacks the `vibe:ai` scope |

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

Test limit: 10 requests per minute (one bucket per portal).

## Known specifics

**An invalid key is not an endpoint error.** If the provider rejected the key — the endpoint returns `200 OK` with `success: true` and `data.valid: false`. HTTP errors (`4xx`/`5xx`) are returned only for problems with the request itself (no key, no access, nothing to decrypt). The single exception is a regional block: the provider rejected the check based on our server's region, and the endpoint answers `422 provider_geoblocked`.

**Writing the result to `lastError`.** The endpoint updates the key's `lastError` and `lastErrorAt` fields in the database. On success it resets them; on failure it records the provider's error text. These fields are visible in [`GET /v1/ai/credentials`](./list.md).

**Verification timeout — 10 seconds.** If the provider does not respond within 10 seconds — `valid: false` with a timeout message.

**The Custom provider is verified against the key's `baseUrl`.** For `custom-openai-compat`, verification goes against the `baseUrl` saved in the credentials. If the provider has no `GET /v1/models` — verification is performed via `POST /v1/chat/completions`.

## See also

- [Key list](./list.md)
- [Update a key](./update.md)
- [Connect a key](./create.md)
