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

# Authentication

> Learn how API keys work and how to manage them securely

## Overview

Satori uses API keys for authentication, powered by Clerk's API key management system. Each API key is tied to a specific tenant (you, the developer), and all memories stored using that key are isolated to your account.

## API Key Structure

Satori API keys follow this format:

```
sk_satori_[random_string]
```

<Warning>
  API keys are secrets that grant full access to your memory data. Never commit them to version control or expose them in client-side code.
</Warning>

## Creating an API Key

<Steps>
  <Step title="Sign in to your dashboard">
    Visit [satori.dev/dashboard](https://satori.dev/dashboard) and sign in with your account.
  </Step>

  <Step title="Navigate to API Keys">
    Click on "API Keys" in the sidebar navigation.
  </Step>

  <Step title="Create a new key">
    Click "Create API Key" and give it a descriptive name:

    ```
    Production Key
    Development Key
    Testing Key
    ```

    <Tip>
      Use different keys for different environments (development, staging, production) to easily revoke access if needed.
    </Tip>
  </Step>

  <Step title="Copy your key">
    Your API key will be displayed once. Copy it immediately and store it securely.

    <Warning>
      You won't be able to see the key again after closing this dialog. If you lose it, you'll need to create a new one.
    </Warning>
  </Step>

  <Step title="Add to environment variables">
    Store your API key in your environment variables:

    ```bash .env.local theme={null}
    SATORI_API_KEY=sk_satori_...
    SATORI_URL=https://api.usesatori.sh
    ```
  </Step>
</Steps>

## Using API Keys

### Server-Side Usage (Recommended)

Always use API keys on the server side, never in client-side code:

```typescript app/api/chat/route.ts theme={null}
import { memoryTools } from '@usesatori/tools';

export async function POST(req: Request) {
  // ✅ Safe: Server-side API route
  const tools = memoryTools({
    apiKey: process.env.SATORI_API_KEY!,
    baseUrl: process.env.SATORI_URL!,
    userId: 'user-123',
  });
  
  // ... rest of your code
}
```

### Client-Side (Never Do This)

<Warning>
  Never expose your API key in client-side code:
</Warning>

```typescript theme={null}
// ❌ NEVER DO THIS
const tools = memoryTools({
  apiKey: 'sk_satori_...', // Exposed to users!
  baseUrl: 'https://api.usesatori.sh',
  userId: 'user-123',
});
```

## Authentication Headers

When making direct HTTP requests to the Satori API, include your API key in the `x-api-key` header:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const response = await fetch('https://api.usesatori.sh/trpc/memory.search', {
    method: 'GET',
    headers: {
      'x-api-key': process.env.SATORI_API_KEY!,
      'Content-Type': 'application/json',
    },
  });
  ```

  ```bash cURL theme={null}
  curl -X GET 'https://api.usesatori.sh/trpc/memory.search' \
    -H 'x-api-key: sk_satori_...' \
    -H 'Content-Type: application/json'
  ```

  ```python Python theme={null}
  import requests

  response = requests.get(
      'https://api.usesatori.sh/trpc/memory.search',
      headers={
          'x-api-key': os.environ['SATORI_API_KEY'],
          'Content-Type': 'application/json'
      }
  )
  ```
</CodeGroup>

## Tenant Isolation Model

Satori uses a two-level isolation model:

```mermaid theme={null}
graph TD
    APIKey[API Key] --> Tenant[Tenant: clerkUserId]
    Tenant --> User1[User: alice]
    Tenant --> User2[User: bob]
    Tenant --> User3[User: carol]
    
    User1 --> Memories1[Alice's Memories]
    User2 --> Memories2[Bob's Memories]
    User3 --> Memories3[Carol's Memories]
```

### Level 1: Tenant (API Key Owner)

Your API key identifies you as the tenant. All memories created with your key belong to your account.

```typescript theme={null}
// When you create an API key in the dashboard
const apiKey = await clerkClient.apiKeys.create({
  name: 'Production Key',
  subject: 'user_372Icb...', // Your Clerk user ID
});
```

### Level 2: End Users (Your Application's Users)

Within your tenant, you can have unlimited end users, each with isolated memories:

```typescript theme={null}
// Alice's memories
const aliceTools = memoryTools({
  apiKey: process.env.SATORI_API_KEY!,
  baseUrl: process.env.SATORI_URL!,
  userId: 'alice', // Your app's user identifier
});

// Bob's memories (completely separate)
const bobTools = memoryTools({
  apiKey: process.env.SATORI_API_KEY!,
  baseUrl: process.env.SATORI_URL!,
  userId: 'bob',
});
```

<Info>
  Think of it like this: Your API key is your "account", and `userId` is how you separate your users' data within your account.
</Info>

## API Key Verification Flow

Here's what happens when you make a request:

<Steps>
  <Step title="Request includes API key">
    Your application sends a request with the `x-api-key` header:

    ```typescript theme={null}
    headers: {
      'x-api-key': 'sk_satori_...'
    }
    ```
  </Step>

  <Step title="Satori verifies with Clerk">
    The Satori server verifies the key with Clerk:

    ```typescript theme={null}
    const verified = await clerkClient.apiKeys.verify(apiKey);
    // verified.subject = "user_372Icb..." (your tenant ID)
    ```
  </Step>

  <Step title="Request is scoped to tenant">
    All database queries are automatically scoped to your tenant:

    ```typescript theme={null}
    const memories = await db
      .select()
      .from(memories)
      .where(
        and(
          eq(memories.clerkUserId, 'user_372Icb...'), // Your tenant
          eq(memories.userId, 'alice') // Specific user
        )
      );
    ```
  </Step>
</Steps>

## Managing API Keys

### List Your Keys

View all your active API keys:

```typescript theme={null}
import { trpc } from '@/lib/trpc';

function APIKeysPage() {
  const { data: keys } = trpc.keys.list.useQuery();
  
  return (
    <div>
      {keys?.map((key) => (
        <div key={key.id}>
          <h3>{key.name}</h3>
          <p>Created: {key.createdAt}</p>
          <p>Last used: {key.lastUsedAt}</p>
        </div>
      ))}
    </div>
  );
}
```

### Revoke a Key

Revoke an API key to immediately prevent all access:

```typescript theme={null}
const revokeKey = trpc.keys.revoke.useMutation();

await revokeKey.mutateAsync({ id: 'key-uuid' });
```

<Warning>
  Revoking a key immediately stops all applications using that key. Make sure to update your applications with a new key before revoking the old one.
</Warning>

## Security Best Practices

<AccordionGroup>
  <Accordion title="Use environment variables">
    Store API keys in environment variables, never hardcode them:

    ```typescript theme={null}
    // ✅ Good
    apiKey: process.env.SATORI_API_KEY!

    // ❌ Bad
    apiKey: 'sk_satori_...'
    ```
  </Accordion>

  <Accordion title="Rotate keys regularly">
    Create a new API key and update your applications, then revoke the old key:

    1. Create new key in dashboard
    2. Update environment variables in all environments
    3. Deploy updates
    4. Revoke old key
  </Accordion>

  <Accordion title="Use different keys per environment">
    Separate keys for development, staging, and production:

    ```bash theme={null}
    # .env.development
    SATORI_API_KEY=sk_satori_dev_...

    # .env.production
    SATORI_API_KEY=sk_satori_prod_...
    ```
  </Accordion>

  <Accordion title="Monitor key usage">
    Check the "Last used" timestamp in your dashboard to detect unused or compromised keys.
  </Accordion>

  <Accordion title="Never commit keys to Git">
    Add environment files to `.gitignore`:

    ```bash .gitignore theme={null}
    .env.local
    .env*.local
    .env.development.local
    .env.production.local
    ```
  </Accordion>
</AccordionGroup>

## Rate Limiting

API keys are subject to rate limits to prevent abuse:

| Limit Type          | Value     |
| ------------------- | --------- |
| Requests per minute | 100       |
| Memories per user   | Unlimited |
| Concurrent requests | 10        |

<Info>
  Need higher limits? Contact support to discuss enterprise plans.
</Info>

## Error Handling

Handle authentication errors gracefully:

```typescript theme={null}
try {
  const memories = await client.searchMemories('query');
} catch (error) {
  if (error.message.includes('Unauthorized')) {
    console.error('Invalid API key');
    // Notify admin, rotate key, etc.
  } else if (error.message.includes('rate limit')) {
    console.error('Rate limit exceeded');
    // Implement backoff strategy
  }
}
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Memory Isolation" icon="shield" href="/concepts/memory-isolation">
    Learn more about tenant and user isolation
  </Card>

  <Card title="API Reference" icon="key" href="/api-reference/keys/create">
    Explore the API key management endpoints
  </Card>

  <Card title="Integration Guide" icon="code" href="/guides/vercel-ai-sdk">
    See authentication in action
  </Card>

  <Card title="Troubleshooting" icon="wrench" href="/troubleshooting">
    Fix common authentication issues
  </Card>
</CardGroup>
