
## List your keys

`GET /v1/search/credentials`

Returns all of the current user's BYOK keys with the key body hidden. The server stores keys encrypted — the original value cannot be returned, only a mask.

## Parameters

No parameters.

## Examples

### curl — personal key

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

### curl — OAuth app

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

### JavaScript — personal key

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/search/credentials', {
  headers: { 'X-Api-Key': 'YOUR_API_KEY' },
})

const { credentials } = await res.json()
credentials.forEach((c) => {
  console.log(`${c.provider} · ${c.maskedKey} · ${c.isDefault ? 'default' : 'regular'}`)
})
```

### JavaScript — OAuth app

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/search/credentials', {
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
  },
})

const { credentials } = await res.json()
```

## Response fields

| Field | Type | Description |
|------|-----|---------|
| `credentials` | array | Array of the user's BYOK keys. May be empty |
| `credentials[].id` | string | Key identifier. Passed to [`DELETE /v1/search/credentials/:id`](/docs/search/credentials/delete) and [`POST /v1/search/credentials/:id/test`](/docs/search/credentials/test) |
| `credentials[].provider` | string | Provider identifier: one of the eight BYOK (`tavily`, `brave`, `exa`, `you-com`, `linkup`, `perplexity`, `jina`, `z-ai`) |
| `credentials[].scope` | string | Key visibility level. In v1, only `USER` is returned |
| `credentials[].name` | string | Arbitrary key label |
| `credentials[].maskedKey` | string | Mask `<first 5>********<last 4>`. The full key cannot be obtained through the API |
| `credentials[].isDefault` | boolean | Considered default for the provider. Used when the `provider` field is omitted in [`POST /v1/search`](/docs/search/run) |
| `credentials[].lastVerifiedAt` | string \| null | Date of the last successful check via [`POST /v1/search/credentials/:id/test`](/docs/search/credentials/test) in ISO 8601 format |
| `credentials[].lastError` | string \| null | Text of the last verification error. `null` if the last verification succeeded or there were none |
| `credentials[].createdAt` | string | Date the key was added, in ISO 8601 format |

## Response example

```json
{
  "credentials": [
    {
      "id": "cmol3pnk5001fo70zefbwb6dl",
      "provider": "tavily",
      "scope": "USER",
      "name": "My Tavily",
      "maskedKey": "tvly-********a1b2",
      "isDefault": true,
      "lastVerifiedAt": "2026-04-30T06:30:00.000Z",
      "lastError": null,
      "createdAt": "2026-04-30T06:26:51.653Z"
    }
  ]
}
```

Empty list:

```json
{ "credentials": [] }
```

## Error response example

401 — no `Authorization: Bearer` passed for an OAuth key:

```json
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "No user context."
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|---------|
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header is missing |
| 401 | `INVALID_API_KEY` | Invalid API key |
| 401 | `UNAUTHORIZED` | The request goes through a `vibe_app_…` key without `Authorization: Bearer <session_token>` |
| 403 | `SCOPE_DENIED` | The key lacks the `vibe:search` scope |
| 429 | `RATE_LIMITED` | The overall request limit is exceeded |
| 500 | `INTERNAL_ERROR` | Internal server error |

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

## Known specifics

**Recovering a lost key.** The original value cannot be obtained from the system — the only option after losing a token is to delete the record via [`DELETE /v1/search/credentials/:id`](/docs/search/credentials/delete) and add the key again.

**`lastVerifiedAt` is updated only via `test`.** Ordinary search requests via [`POST /v1/search`](/docs/search/run) do not move the date — even if the key was actually used successfully. For a fresh timestamp call [`POST /v1/search/credentials/:id/test`](/docs/search/credentials/test).

## See also

- [Your own keys (BYOK)](/docs/search/credentials) — subsection overview
- [Add a key](/docs/search/credentials/create) — `POST /v1/search/credentials`
- [Delete a key](/docs/search/credentials/delete) — `DELETE /v1/search/credentials/:id`
- [Test a key](/docs/search/credentials/test) — `POST /v1/search/credentials/:id/test`
- [Search (POST /v1/search)](/docs/search/run) — BYOK key selection cascade
- [Errors](/docs/errors) — API error code reference
