> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# JS Credential Rotation

> Update OPENAI_API_KEY or OPENAI_BASE_URL at runtime without restarting the process

`praisonai-ts` picks up a changed `OPENAI_API_KEY` or `OPENAI_BASE_URL` on the next call — no restart, no code change.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "Env-only client cache"
        Call[📞 next agent.chat] --> Id{🔑 apiKey + baseURL changed?}
        Id -->|no| Reuse[♻️ Reuse cached client]
        Id -->|yes| Rebuild[🔨 Rebuild client]
        Rebuild --> Fresh[✅ Fresh client]
    end

    classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef process fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef config fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff

    class Call input
    class Id config
    class Rebuild process
    class Reuse,Fresh output
```

The env-only OpenAI client caches on **client identity** (`apiKey` + `baseURL`). Rotate either and the next call rebuilds automatically — the exact flow a settings screen needs when a user pastes a new key.

## Quick Start

**1. Rotate the key in your settings screen**

Set the env var; the next call uses the new key.

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import { Agent } from 'praisonai';

process.env.OPENAI_API_KEY = 'sk-new';
const agent = new Agent({ instructions: 'You are helpful' });
await agent.chat('Hello');   // uses sk-new — no restart
```

**2. Point at a different gateway**

Set `OPENAI_BASE_URL`; the next call rebuilds against the new endpoint.

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
process.env.OPENAI_BASE_URL = 'https://gateway.example/v1';
await agent.chat('Hello via gateway');   // rebuilt against the new endpoint
```

**3. Force a rebuild explicitly**

Call `resetOpenAIClient()` to rebuild on the next call — useful in tests and after a bulk settings change.

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import { resetOpenAIClient } from 'praisonai';

resetOpenAIClient();
await agent.chat('Fresh client');
```

***

## How It Works

The env-only client keys its cache on `apiKey + baseURL`. A settings-screen edit to either env var changes that identity, so the next `getOpenAIClient()` rebuilds.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant Settings as Settings screen
    participant Env as process.env
    participant Agent
    participant Client as getOpenAIClient()

    User->>Settings: Paste new key
    Settings->>Env: OPENAI_API_KEY = sk-new
    User->>Agent: agent.chat("Hi")
    Agent->>Client: request client
    Client->>Client: identity changed → rebuild
    Client-->>Agent: fresh client (sk-new)
    Agent-->>User: Response
```

| Trigger | Result |
| - | - |
| `OPENAI_API_KEY` changes between calls | Rebuild on next call |
| `OPENAI_BASE_URL` changes between calls | Rebuild on next call |
| Nothing changes | Reuse cached client |
| `resetOpenAIClient()` called | Rebuild on next call |

> [!NOTE]
>
> `OPENAI_BASE_URL` is honoured in the env-only path. Exporting it works for the module-level convenience functions and any `Agent` built from env only — the same way an explicit `baseURL` on the config already did.

***

## When Does This Apply?

Rotation applies only to the shared env-only client. Per-agent credentials never touch that cache.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    Start{How are credentials supplied?} -->|Per-agent apiKey / baseURL on config| PerAgent[Dedicated client — nothing to rotate]
    Start -->|Env vars only Agent or convenience function| Cached[Shared env-only client — rotation applies]
    classDef q fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef a fill:#10B981,stroke:#7C90A0,color:#fff
    class Start q
    class PerAgent,Cached a
```

An `Agent` built with an explicit `apiKey` or `baseURL` gets its own dedicated client, so a later env-var change does not affect it. Rotate by constructing a new `Agent` with the new value.

***

## Concurrency

A `resetOpenAIClient()` or credential change that races an in-flight request is safe. Each caller keeps the client it built for its own identity — an overlapping call that swaps the key never hands back another call's client, and a reset mid-flight never yields `null`.

***

## Security

The identity that keys the cache contains the secret key. `praisonai-ts` never logs it — do not add logging that prints it either.

***

## API Reference

Two exports back the rotation contract. The recommended user surface stays `new Agent(...)` and `agent.chat(...)`.

| Export | Signature | When to call |
| - | - | - |
| `resetOpenAIClient` | `() => void` | Tests; explicit "apply now" from a settings screen; recovery after a suspected stale-cache bug in older versions. |

> [accordion] Advanced: getOpenAIClient()
>
> `getOpenAIClient(): Promise<OpenAI>` is exported so tests and advanced integrations can reach the shared env-only client. It throws `OPENAI_API_KEY not found in environment variables` when no key is set. Prefer `new Agent(...)` for application code.

***

## Common Patterns

**Simple rotation — no code change from the SDK caller**

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import { Agent } from 'praisonai';

process.env.OPENAI_API_KEY = 'sk-first';
const agent = new Agent({ instructions: 'You are helpful' });
await agent.chat('Hello');           // uses sk-first

// Settings screen updates the env var:
process.env.OPENAI_API_KEY = 'sk-second';
await agent.chat('Hello again');     // rebuilds automatically; uses sk-second
```

**Change the gateway at runtime**

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import { Agent } from 'praisonai';

process.env.OPENAI_API_KEY = 'sk-shared';
process.env.OPENAI_BASE_URL = 'https://gateway-a.example/v1';
const agent = new Agent({ instructions: 'You are helpful' });
await agent.chat('Hello via gateway A');

process.env.OPENAI_BASE_URL = 'https://gateway-b.example/v1';
await agent.chat('Hello via gateway B');   // rebuilt against gateway B
```

**Explicit rebuild — tests and bulk settings updates**

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import { Agent, resetOpenAIClient } from 'praisonai';

process.env.OPENAI_API_KEY = 'sk-stable';
const agent = new Agent({ instructions: 'You are helpful' });
await agent.chat('One');

resetOpenAIClient();                   // force next call to rebuild
await agent.chat('Two');               // fresh client, same env
```

***

## Best Practices

> [accordion] Prefer per-agent credentials in multi-tenant apps
>
> Pass `apiKey` / `baseURL` on each `Agent` to isolate tenants. Each agent gets its own client, so one tenant's rotation never affects another.

> [accordion] Trust the automatic rebuild in single-tenant apps
>
> The next call rebuilds when the key or base URL changes. There is no need to call `resetOpenAIClient()` on every key update.

> [accordion] Never log the identity string or API key
>
> The cache identity embeds the secret. Keep it out of logs and error messages.

> [accordion] One reset after a bulk settings change
>
> After changing several settings at once in a UI, a single `resetOpenAIClient()` is enough — the next call rebuilds once.

***

## Related

> [card] [Agent](https://praison.ai/docs/js/agent.md)
>
> The core Agent class and credential options

> [card] [Import Safety (JS)](https://praison.ai/docs/features/js-import-safety.md)
>
> Lazy credential reads and runtime safety

> [card] [Browser & Webview Runtimes](https://praison.ai/docs/features/browser-runtimes.md)
>
> Run agents in browsers, Tauri, Electron, and React Native


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.