# How to Set Up Encrypted Credentials in Agent-Native Using `saveCredential` and `resolveCredential`

> Secure your AI agent with Agent-Native's encrypted credentials. Learn to use saveCredential and resolveCredential for seamless AES-256-GCM encryption and decryption.

- Repository: [Builder.io/agent-native](https://github.com/BuilderIO/agent-native)
- Tags: how-to-guide
- Published: 2026-07-02

---

**Agent-Native automatically encrypts API keys and secrets using AES-256-GCM before storing them in the SQL `settings` table, exposing two helper functions—`saveCredential` and `resolveCredential`—to handle scoping, encryption, and decryption transparently.**

The `BuilderIO/agent-native` repository provides a credentials subsystem that eliminates the need to manage environment variables for user-specific secrets. Instead of storing plaintext tokens, you use the context-aware helpers defined in [`packages/core/src/credentials/index.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/credentials/index.ts) to persist encrypted values scoped to either individual users or entire organizations.

## Understanding the Credential Encryption Architecture

Before calling the API functions, you need to understand how Agent-Native structures credential storage and encryption.

### CredentialContext and Storage Keys

Every credential operation requires a **`CredentialContext`** object containing at minimum a `userEmail` string. Optionally, you can include an `orgId` to enable organization-scoped storage.

The system generates storage keys using these conventions:
- **User-scoped**: `u:<email>:credential:<KEY>`
- **Org-scoped**: `o:<orgId>:credential:<KEY>`

These keys map to rows in the `settings` table, where the value is encrypted using `encryptSecretValue` from [`packages/core/src/secrets/crypto.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/secrets/crypto.ts) before persistence.

### AES-256-GCM Encryption Flow

When you call `saveCredential`, the plain text value is passed to `encryptSecretValue`, which applies **AES-256-GCM** encryption. The ciphertext is then stored via the low-level settings API in [`packages/core/src/settings/store.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/settings/store.ts). During retrieval, `resolveCredential` automatically decrypts the value using the corresponding decryption helper, returning the original plaintext to your application code.

## Saving Credentials with `saveCredential`

The `saveCredential` function persists encrypted secrets and accepts three parameters: the credential key name, the raw secret value, and the context object.

### User-Scoped Credentials (Default)

By default, credentials are tied to the individual user specified in the context:

```typescript
import { saveCredential } from '@agent-native/core/credentials';

async function storeOpenAiKey(userEmail: string, rawKey: string) {
  await saveCredential('OPENAI_API_KEY', rawKey, {
    userEmail,
    // orgId omitted — stores under u:<email>:credential:OPENAI_API_KEY
  });
}

```

### Organization-Scoped Credentials

To share credentials across an entire organization, explicitly pass `scope: "org"`:

```typescript
await saveCredential('STRIPE_SECRET', stripeKey, {
  userEmail: currentUser.email,
  orgId: currentOrg.id,
  scope: 'org',  // Stores under o:<orgId>:credential:STRIPE_SECRET
});

```

Organization-scoped values are accessible to all users within that organization unless a user has a personal override stored.

## Retrieving Credentials with `resolveCredential`

The `resolveCredential` function reads credentials using a hierarchical lookup strategy defined in [`packages/core/src/credentials/index.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/credentials/index.ts):

1. Check for a user-specific credential (`u:<email>:credential:<KEY>`)
2. If not found and `orgId` is present, check for an org-scoped credential (`o:<orgId>:credential:<KEY>`)
3. Return `undefined` if no credential exists

Unlike some credential managers, this implementation **does not** fall back to environment variables.

```typescript
import { resolveCredential } from '@agent-native/core/credentials';

async function getOpenAiKey(ctx: { userEmail: string; orgId?: string }) {
  const apiKey = await resolveCredential('OPENAI_API_KEY', ctx);
  if (!apiKey) throw new Error('OpenAI API key not configured');
  return apiKey;
}

```

For simple presence checks without retrieving the value, use the `hasCredential` helper, which returns a boolean based on the same resolution logic.

## Real-World Implementation Examples

The Agent-Native templates demonstrate production usage of these credentials helpers across different integration patterns.

### Analytics API Endpoint

In [`templates/analytics/server/routes/api/credentials.post.ts`](https://github.com/BuilderIO/agent-native/blob/main/templates/analytics/server/routes/api/credentials.post.ts), an HTTP endpoint exposes credential storage to the frontend:

```typescript
import { saveCredential } from '@agent-native/core/credentials';
import { getUserContext } from '@/server/context';

export async function POST(req: Request) {
  const { key, value } = await req.json();
  const ctx = await getUserContext(req);  // { userEmail, orgId? }
  
  await saveCredential(key, value, ctx);
  return new Response('Credential encrypted and saved');
}

```

### Calendar Integration Credentials

The calendar template in [`templates/calendar/server/lib/integration-credentials.ts`](https://github.com/BuilderIO/agent-native/blob/main/templates/calendar/server/lib/integration-credentials.ts) shows both storage and retrieval patterns for third-party provider keys:

```typescript
// Storage
await saveCredential(credentialKey(provider), apiKey, ctx);

// Retrieval (elsewhere in the codebase)
const apiKey = await resolveCredential('GOOGLE_API_KEY', ctx);
if (!apiKey) throw new Error('Google API key missing');

```

### Mail/Google OAuth Configuration

For OAuth flows requiring client secrets, [`templates/mail/server/lib/google-auth.ts`](https://github.com/BuilderIO/agent-native/blob/main/templates/mail/server/lib/google-auth.ts) uses `resolveCredential` to fetch the Google client ID and secret without exposing them to the client bundle:

```typescript
const clientId = await resolveCredential('GOOGLE_OAUTH_CLIENT_ID', ctx);
const clientSecret = await resolveCredential('GOOGLE_OAUTH_CLIENT_SECRET', ctx);

```

## Summary

- **`saveCredential`** in [`packages/core/src/credentials/index.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/credentials/index.ts) encrypts values with AES-256-GCM and stores them in the SQL `settings` table using user-scoped (`u:*`) or org-scoped (`o:*`) keys.
- **`resolveCredential`** implements a fallback hierarchy: user-specific credentials take precedence over organization-scoped values.
- **Scoped storage** is controlled via the `scope` parameter (`"user"` default or `"org"`) and the presence of `orgId` in the context.
- **Template implementations** in Analytics, Calendar, and Mail demonstrate production-ready patterns for both HTTP endpoints and library utilities.

## Frequently Asked Questions

### What encryption algorithm does Agent-Native use for credentials?

Agent-Native uses **AES-256-GCM** (Galois/Counter Mode) via the `encryptSecretValue` function in [`packages/core/src/secrets/crypto.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/secrets/crypto.ts). This provides authenticated encryption, ensuring both confidentiality and integrity of stored secrets.

### Can I store credentials without a user context?

No. The `CredentialContext` requires at minimum a `userEmail` to generate the storage key (`u:<email>:credential:*`). While you can store organization-scoped credentials that are shared across users, the operation itself must be initiated with a valid user context to maintain audit trails and access control.

### How does `resolveCredential` handle missing credentials?

If a credential is not found in either the user-specific or organization-scoped store, `resolveCredential` returns `undefined`. It does not throw an error or fall back to environment variables. Your application code must explicitly check for `undefined` and handle the missing credential case appropriately.

### Where are the encrypted values physically stored?

Encrypted values are stored in the **`settings` table** of your configured SQL database (SQLite, PostgreSQL, etc.). The raw encryption keys are never persisted alongside the ciphertext; decryption requires the application-level secret key configured in your Agent-Native environment.