Guides

Authentication

API keys and frontend tokens

Learn how to authenticate with EasyRAG using API keys and frontend tokens.

Two Authentication Methods

EasyRAG offers two ways to authenticate:

  1. API Keys - For backend/server-side applications
  2. Frontend Tokens - For browser/mobile applications

When to Use Each Method

Use API Keys When:

  • Building a backend service
  • Creating server-side integrations
  • Running scripts or cron jobs
  • You have a secure environment to store secrets

✅ API keys have full access to your account

Use Frontend Tokens When:

  • Building a browser-based application
  • Creating a mobile app
  • You need to make API calls from the client-side
  • You want to avoid exposing your API key

✅ Frontend tokens are short-lived and dataset-scoped

API Keys

Getting Your API Key

  1. Log into easyrag.com
  2. Go to Settings → API Keys
  3. Click Generate API Key
  4. Copy and save it (shown only once!)

Your API key looks like: sk_abc123xyz...

Using API Keys

Include your API key in the Authorization header:

bash
curl https://api.easyrag.com/v1/files?datasetId=my-dataset \ -H "Authorization: Bearer sk_abc123xyz..."

Or use the custom header:

bash
curl https://api.easyrag.com/v1/files?datasetId=my-dataset \ -H "x-easyrag-key: sk_abc123xyz..."

JavaScript Example

javascript
const apiKey = process.env.EASYRAG_API_KEY; const response = await fetch('https://api.easyrag.com/v1/files?datasetId=my-dataset', { headers: { 'Authorization': `Bearer ${apiKey}` } });

Security Best Practices

✅ DO:

  • Store API keys in environment variables
  • Use different keys for dev/staging/production
  • Rotate keys periodically
  • Keep keys on the server-side only

❌ DON'T:

  • Commit API keys to Git
  • Expose API keys in frontend code
  • Share API keys publicly
  • Hardcode API keys in your source code
javascript
// ❌ BAD: Hardcoded in frontend const apiKey = 'sk_abc123xyz...'; // ✅ GOOD: Server-side only // backend.js const apiKey = process.env.EASYRAG_API_KEY;

Frontend Tokens

Frontend tokens solve the problem: "How do I use EasyRAG from my frontend without exposing my API key?"

How It Works

1. User logs into your app
   ↓
2. Your backend verifies the user
   ↓
3. Your backend calls EasyRAG to generate a token
   ↓
4. You send the token to your frontend
   ↓
5. Frontend uses token for API calls

Token Properties

  • Short-lived: Max 24 hours (default: 1 hour)
  • Dataset-scoped: Only works with one dataset
  • Safe for browsers: Can be exposed in client-side code
  • Generated server-side: Requires API key to create

Creating a Frontend Token

Endpoint: POST /v1/tokens/create

Requirements:

  • Must use API key (tokens cannot create tokens)
  • Must specify a datasetId
  • Optionally specify ttlSeconds (max 86400)

Request

bash
curl -X POST https://api.easyrag.com/v1/tokens/create \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "datasetId": "user-123-docs", "ttlSeconds": 3600 }'

Response

json
{ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expiresIn": 3600 }

Creating a Frontend Token with the SDK (Node.js)

Instead of calling the HTTP endpoint directly, you can also use the official EasyRAG SDK from your backend:

ts
import { EasyRAG } from "@easyrag/sdk"; const client = new EasyRAG({ apiKey: process.env.EASY_RAG_KEY!, }); const { token, expiresIn } = await client.createToken("user-123-docs", { ttlSeconds: 3600, });

The returned token is exactly the same type of frontend token you would get from POST https://api.easyrag.com/v1/tokens/create.

Complete Flow Example (HTTP API)

Here's how to implement this in your app:

Backend (Node.js/Express)

javascript
// backend/routes/token.js app.post('/api/get-easyrag-token', authenticateUser, async (req, res) => { const userId = req.user.id; const { datasetId } = req.body; // 1. Verify user owns this dataset (your logic) const hasAccess = await checkUserDatasetAccess(userId, datasetId); if (!hasAccess) { return res.status(403).json({ error: 'Access denied' }); } // 2. Generate token from EasyRAG const response = await fetch('https://api.easyrag.com/v1/tokens/create', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.EASYRAG_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ datasetId, ttlSeconds: 3600 // 1 hour }) }); const { token, expiresIn } = await response.json(); // 3. Send token to frontend res.json({ token, expiresIn }); });

Frontend (React)

javascript
import { useState, useEffect } from 'react'; function useEasyRAGToken(datasetId) { const [token, setToken] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { async function fetchToken() { try { // Call YOUR backend to get a token const response = await fetch('/api/get-easyrag-token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', // Send cookies body: JSON.stringify({ datasetId }) }); const { token, expiresIn } = await response.json(); setToken(token); // Refresh token before it expires setTimeout(fetchToken, (expiresIn - 60) * 1000); } catch (error) { console.error('Failed to get token:', error); } finally { setLoading(false); } } fetchToken(); }, [datasetId]); return { token, loading }; } // Usage in your component function DocumentUpload({ datasetId }) { const { token, loading } = useEasyRAGToken(datasetId); const uploadFile = async (file) => { if (!token) return; const formData = new FormData(); formData.append('file', file); // datasetId is optional - token contains it const response = await fetch('https://api.easyrag.com/v1/files/upload', { method: 'POST', headers: { 'Authorization': `Bearer ${token}` // Safe to use in browser! }, body: formData }); return await response.json(); }; if (loading) return <div>Loading...</div>; return <FileUploadUI onUpload={uploadFile} />; }

Using Frontend Tokens

Once you have a token, use it like an API key:

javascript
const response = await fetch('https://api.easyrag.com/v1/search', { method: 'POST', headers: { 'Authorization': `Bearer ${frontendToken}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ // datasetId is optional - token includes it question: 'What is the refund policy?' }) });

Dataset Scoping

Frontend tokens are locked to a specific dataset:

javascript
// Token was created for "project-a" const token = await createToken('project-a'); // ✅ Works: Same dataset await uploadFile(token, 'project-a', file); await search(token, 'project-a', query); // ❌ Fails with 403: Different dataset await uploadFile(token, 'project-b', file); // Error: "datasetId mismatch between token and request"

Optional datasetId

When using a frontend token, you can omit the datasetId parameter:

javascript
// Both are equivalent with a frontend token: // Option 1: Omit datasetId (token provides it) await fetch('https://api.easyrag.com/v1/search', { headers: { 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ question: 'hello' }) }); // Option 2: Include matching datasetId await fetch('https://api.easyrag.com/v1/search', { headers: { 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ datasetId: 'project-a', // must match token question: 'hello' }) });

Multi-Tenant Patterns

Pattern 1: Dataset Per User (Recommended)

Create a unique dataset for each user:

javascript
// Backend generates token const datasetId = `user-${userId}`; const response = await fetch('https://api.easyrag.com/v1/tokens/create', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.EASYRAG_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ datasetId, // "user-123" ttlSeconds: 3600 }) });

Benefits:

  • Complete data isolation
  • Simple security model
  • No need for metadata filters

Pattern 2: Shared Dataset with Filters

Use one dataset with metadata filtering:

javascript
// Upload with userId in metadata await upload('shared-dataset', file, { metadata: { [file.name]: { userId: currentUserId } } }); // Search with user filter await search('shared-dataset', question, { filters: [ { key: 'userId', match: { value: currentUserId } } ] });

Benefits:

  • Fewer datasets to manage
  • Good for shared data scenarios

Drawbacks:

  • Must remember to apply filters everywhere
  • More complex security model

Token Lifetime Recommendations

Choose appropriate token lifetimes based on your use case:

Use CaseRecommended TTLReason
Single file upload300s (5 min)Just long enough for upload
Chat session3600s (1 hour)Typical conversation length
Mobile app14400s (4 hours)Longer session time
Admin panel86400s (24 hours)All-day access
javascript
// Short-lived for quick operations const uploadToken = await createToken(datasetId, 300); // 5 min // Longer-lived for sessions const chatToken = await createToken(datasetId, 3600); // 1 hour

Error Responses

401 Unauthorized

json
{ "error": "Missing API key or token" }

Fix: Include Authorization: Bearer YOUR_KEY_OR_TOKEN header

403 Forbidden

json
{ "error": "datasetId mismatch between token and request" }

Fix: Ensure the datasetId in your request matches the token's dataset

"Missing API key" (when creating tokens)

json
{ "error": "Missing API key" }

Fix: The /v1/tokens/create endpoint only accepts API keys, not frontend tokens

Security Checklist

Before deploying to production:

  • ✅ API keys stored in environment variables
  • ✅ Frontend tokens generated server-side only
  • ✅ User authentication before token generation
  • ✅ Dataset access control in your app
  • ✅ Appropriate token TTLs
  • ✅ HTTPS enabled
  • ❌ No API keys in frontend code
  • ❌ No keys committed to Git
  • ❌ No keys in logs

Rate Limits

Rate limits apply per customer (not per key/token):

  • Uploads: 100 files/minute
  • Queries: 1000 requests/minute

Both API keys and frontend tokens share these limits.

Need Help?