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​