Skip to main content

API Key Permissions

Control what API keys can access and do within your Enclava instance.

Permission System Overview

Enclava uses a hierarchical permission system:

  1. Global Permissions - Platform-wide capabilities
  2. Module Permissions - Access to specific features (RAG, Agents, etc.)
  3. Resource Permissions - Access to specific resources (collections, chatbots)
  4. Model Access - Which LLM models can be used

Global Permissions

Available Permissions

PermissionDescriptionDefault for New Keys
api.readRead access to API endpoints
api.writeWrite access to API endpoints
chat_completionsUse chat completion endpoints
embeddingsGenerate embeddings
rag.readRead RAG collections and documents
rag.writeUpload and modify RAG documents
rag.searchSearch RAG collections
chatbot.chatUse chatbot endpoints
chatbot.manageCreate and manage chatbots
agent.chatUse agent endpoints
agent.manageCreate and manage agents
tools.executeUse tool endpoints

Checking Key Permissions

import requests

response = requests.get(
f"http://localhost/api-internal/v1/api-keys/{key_id}",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"}
)

key_info = response.json()
print("Permissions:")
for perm in key_info["permissions"]:
print(f" - {perm}")

Model Access Permissions

Restricting Models

Create keys with specific model access:

response = requests.post(
"http://localhost/api-internal/v1/api-keys",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"name": "GPT-3.5 Only",
"allowed_models": ["gpt-3.5-turbo"]
}
)

Allowing Multiple Models

response = requests.post(
"http://localhost/api-internal/v1/api-keys",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"name": "Production Models",
"allowed_models": ["gpt-4", "gpt-3.5-turbo", "text-embedding-ada-002"]
}
)

Full Model Access

response = requests.post(
"http://localhost/api-internal/v1/api-keys",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"name": "Full Access",
"allowed_models": [] # Empty = all models
}
)

Resource-Level Permissions

RAG Collection Access

Restrict a key to specific RAG collections:

response = requests.post(
"http://localhost/api-internal/v1/api-keys",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"name": "Documentation Reader",
"permissions": ["rag.read", "rag.search"],
"rag_collections": ["docs_collection", "policy_docs"]
}
)

Chatbot Access

Restrict a key to specific chatbots:

response = requests.post(
"http://localhost/api-internal/v1/api-keys",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"name": "Support Bot Key",
"permissions": ["chatbot.chat"],
"chatbot_ids": ["chatbot-123", "chatbot-456"]
}
)

Agent Access

Restrict a key to specific agents:

response = requests.post(
"http://localhost/api-internal/v1/api-keys",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"name": "Research Agent Key",
"permissions": ["agent.chat"],
"agent_ids": ["agent-research", "agent-analysis"]
}
)

Permission Use Cases

Use Case 1: Read-Only Documentation Key

For applications that only need to search documentation:

response = requests.post(
"http://localhost/api-internal/v1/api-keys",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"name": "Doc Search Only",
"permissions": ["rag.search"],
"rag_collections": ["public_docs"],
"allowed_models": ["text-embedding-ada-002"] # Only embeddings
}
)

Use Case 2: Production Chatbot Key

For a live chatbot widget:

response = requests.post(
"http://localhost/api-internal/v1/api-keys",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"name": "Chat Widget Production",
"permissions": ["chatbot.chat"],
"chatbot_ids": ["support-bot-prod"],
"allowed_models": ["gpt-3.5-turbo"], # Cheaper model for chat
"daily_spend_limit": 50.00,
"monthly_spend_limit": 1500.00
}
)

Use Case 3: Internal Analysis Tool

For internal data analysis:

response = requests.post(
"http://localhost/api-internal/v1/api-keys",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"name": "Internal Analysis",
"permissions": [
"chat_completions",
"rag.read",
"rag.search",
"tools.execute"
],
"rag_collections": ["internal_docs", "financial_data"],
"allowed_models": ["gpt-4"],
"no_budget_limit": True # Internal tool, no spend limit
}
)

Use Case 4: External API Key

For exposing limited functionality to external partners:

response = requests.post(
"http://localhost/api-internal/v1/api-keys",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"name": "Partner API Key",
"permissions": ["chatbot.chat"],
"chatbot_ids": ["public-info-bot"],
"allowed_models": ["gpt-3.5-turbo"],
"rag_collections": ["public_content"], # Only public data
"daily_request_limit": 1000, # Rate limit
"daily_spend_limit": 10.00
}
)

Updating Permissions

Add Permissions to Existing Key

response = requests.put(
f"http://localhost/api-internal/v1/api-keys/{key_id}",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"permissions": ["chatbot.chat", "rag.search"] # Add RAG search
}
)

Remove Permissions

response = requests.put(
f"http://localhost/api-internal/v1/api-keys/{key_id}",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"permissions": ["chatbot.chat"] # Remove RAG search
}
)

Change Model Access

response = requests.put(
f"http://localhost/api-internal/v1/api-keys/{key_id}",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"allowed_models": ["gpt-4"] # Change from multiple to single model
}
)

Permission Errors

Common Error Messages

403 Forbidden - Permission Denied

{
"error": "PERMISSION_DENIED",
"message": "API key does not have permission to access this resource"
}

Solution: Add the required permission to the key.

403 Forbidden - Model Not Allowed

{
"error": "MODEL_NOT_ALLOWED",
"message": "API key does not have access to model 'gpt-4'"
}

Solution: Add the model to allowed_models or use an allowed model.

403 Forbidden - Collection Not Allowed

{
"error": "COLLECTION_NOT_ALLOWED",
"message": "API key does not have access to collection 'internal_docs'"
}

Solution: Add the collection to the key's allowed RAG collections.

Debugging Permissions

import requests

# Try to request
response = requests.post(
"http://localhost/api/v1/rag/search",
headers={"Authorization": "Bearer en_xxxxxxxx"},
json={"collection_name": "internal_docs", "query": "test"}
)

# Check if it's a permission error
if response.status_code == 403:
error = response.json()
print(f"Error: {error['error']}")
print(f"Message: {error['message']}")

# Get key details to see current permissions
key_response = requests.get(
f"http://localhost/api-internal/v1/api-keys/{key_id}",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"}
)
print("\nCurrent permissions:")
print(key_response.json()["permissions"])

Best Practices

Principle of Least Privilege

Always grant the minimum permissions needed:

Application TypeRecommended Permissions
Chat widgetchatbot.chat only
Documentation searchrag.read, rag.search only
Data analysischat_completions, rag.search
Full applicationAll needed permissions

Separate Keys for Separate Environments

EnvironmentKey Permissions
DevelopmentFull access, no limits
StagingProduction-like permissions, higher limits
ProductionMinimal permissions, strict limits

Document Permission Changes

Keep track of why permissions were changed:

# When updating permissions, add a note
response = requests.put(
f"http://localhost/api-internal/v1/api-keys/{key_id}",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"permissions": ["chatbot.chat", "rag.search"],
"note": "Added RAG search for improved support bot accuracy"
}
)

Next Steps