Overview
Scope Management allows you to segment data within a chat using custom attributes. This is perfect for multi-tenant applications, playlist-based content, or any scenario where you need to filter queries to specific subsets of documents.
Think of scopes as “tags” or “filters” that you attach to documents and then
use to filter queries.
Use Cases
Multi-Tenant SaaS Filter data by workspace_id or tenant_id
Content Platforms Organize by playlist_id, album_id, or category
Version Control Track by version, branch, or environment
Access Control Segment by user_id, team_id, or permission_level
Scope Configuration
Define Scopes for a Chat
Configure custom scope fields for your chat:
POST /v1/{chat_id}/scopes/configure
curl -X POST https://api.trainlyai.com/v1/chat_abc123/scopes/configure \
-H "Authorization: Bearer tk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"scopes": [
{
"name": "playlist_id",
"type": "string",
"required": true,
"description": "ID of the playlist this document belongs to"
},
{
"name": "workspace_id",
"type": "string",
"required": false,
"description": "Optional workspace identifier"
},
{
"name": "version",
"type": "number",
"required": false,
"description": "Document version number"
},
{
"name": "is_public",
"type": "boolean",
"required": false,
"description": "Whether content is publicly accessible"
}
]
}'
Request Body
Array of scope definitions Show Scope Definition Properties
Scope field name (alphanumeric, underscores, hyphens)
Data type: string, number, or boolean
Whether this scope must be provided on upload
Human-readable description of the scope
Response
{
"success" : true ,
"message" : "Scope configuration saved successfully" ,
"chat_id" : "chat_abc123" ,
"scopes" : [
{
"name" : "playlist_id" ,
"type" : "string" ,
"required" : true ,
"description" : "ID of the playlist this document belongs to"
},
{
"name" : "workspace_id" ,
"type" : "string" ,
"required" : false ,
"description" : "Optional workspace identifier"
}
]
}
Get Scope Configuration
Retrieve current scope configuration:
curl -X GET https://api.trainlyai.com/v1/chat_abc123/scopes \
-H "Authorization: Bearer tk_your_api_key"
Response
{
"chat_id" : "chat_abc123" ,
"scopes" : [
{
"name" : "playlist_id" ,
"type" : "string" ,
"required" : true ,
"description" : "ID of the playlist this document belongs to"
},
{
"name" : "workspace_id" ,
"type" : "string" ,
"required" : false ,
"description" : "Optional workspace identifier"
}
]
}
Delete Scope Configuration
Clear all scope configurations:
DELETE /v1/{chat_id}/scopes
curl -X DELETE https://api.trainlyai.com/v1/chat_abc123/scopes \
-H "Authorization: Bearer tk_your_api_key"
This only removes the configuration, not the scope data on existing documents.
Response
{
"success" : true ,
"message" : "Scope configuration cleared" ,
"chat_id" : "chat_abc123"
}
Upload with Scopes
Upload File with Scope Values
Attach scope values when uploading documents:
POST /v1/{chat_id}/upload_with_scopes
curl -X POST https://api.trainlyai.com/v1/chat_abc123/upload_with_scopes \
-H "Authorization: Bearer tk_your_api_key" \
-F "file=@song_lyrics.pdf" \
-F 'scope_values={"playlist_id":"playlist_summer_2024","workspace_id":"ws_acme","is_public":true}'
Request Body (Multipart Form)
JSON string of scope key-value pairs matching your configuration
Response
{
"success" : true ,
"filename" : "song_lyrics.pdf" ,
"file_id" : "chat_abc123_song_lyrics.pdf_1609459200" ,
"chat_id" : "chat_abc123" ,
"scope_values" : {
"playlist_id" : "playlist_summer_2024" ,
"workspace_id" : "ws_acme" ,
"is_public" : true
},
"size_bytes" : 204800 ,
"message" : "File uploaded successfully with custom scope values"
}
V1 Upload with Scopes
For V1 authenticated users:
POST /v1/me/chats/files/upload
curl -X POST https://api.trainlyai.com/v1/me/chats/files/upload \
-H "Authorization: Bearer <USER_OAUTH_ID_TOKEN>" \
-H "X-App-ID: app_your_app_id" \
-F "file=@document.pdf" \
-F 'scope_values={"playlist_id":"playlist_123","category":"research"}'
Query with Scope Filters
Filter Queries by Scopes
Only search within documents matching specific scope values:
POST /v1/{chat_id}/answer_question
curl -X POST https://api.trainlyai.com/v1/chat_abc123/answer_question \
-H "Authorization: Bearer tk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"question": "What are the song lyrics?",
"scope_filters": {
"playlist_id": "playlist_summer_2024",
"is_public": true
}
}'
Request Body
Key-value pairs to filter documents. Only documents with matching scope values
will be searched.
Response
{
"answer" : "The lyrics include..." ,
"context" : [
{
"chunk_id" : "doc1_chunk_0" ,
"chunk_text" : "Verse 1: ..." ,
"score" : 0.94
}
],
"scope_filters_applied" : {
"playlist_id" : "playlist_summer_2024" ,
"is_public" : true
}
}
V1 Query with Scopes
curl -X POST https://api.trainlyai.com/v1/me/chats/query \
-H "Authorization: Bearer <USER_OAUTH_ID_TOKEN>" \
-H "X-App-ID: app_your_app_id" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "messages=[{ \" role \" : \" user \" , \" content \" : \" What songs are in this playlist? \" }]" \
-d 'scope_filters={"playlist_id":"playlist_summer_2024"}'
Scope Validation
Automatic Validation
Trainly automatically validates scope values against your configuration:
Name Validation
Scope names must start with a letter and contain only alphanumeric,
underscores, or hyphens (max 64 chars)
Type Validation
Values must match the configured type (string, number, or boolean)
Required Check
Required scopes must be present on upload
Length Limits
String values are limited to 255 characters
Validation Examples
Valid Scope Names
✅ playlist_id
✅ workspace123
✅ user-role
✅ version_2_0
Invalid Scope Names
❌ 123playlist (starts with number)
❌ user@email (invalid character)
❌ workspace$id (invalid character)
❌ my scope (spaces not allowed)
Implementation Examples
Multi-Tenant SaaS
// Upload document for tenant
await fetch ( "https://api.trainlyai.com/v1/chat_abc/upload_with_scopes" , {
method: "POST" ,
headers: {
Authorization: "Bearer tk_api_key" ,
},
body: formData ({
file: document ,
scope_values: JSON . stringify ({
tenant_id: "tenant_acme" ,
workspace_id: "ws_engineering" ,
access_level: "internal" ,
}),
}),
});
// Query only tenant's data
const response = await fetch (
"https://api.trainlyai.com/v1/chat_abc/answer_question" ,
{
method: "POST" ,
headers: {
Authorization: "Bearer tk_api_key" ,
"Content-Type" : "application/json" ,
},
body: JSON . stringify ({
question: "What are our Q4 goals?" ,
scope_filters: {
tenant_id: "tenant_acme" ,
workspace_id: "ws_engineering" ,
},
}),
},
);
Content Platform with Playlists
// Upload song to playlist
await uploadWithScopes ({
file: songFile ,
scope_values: {
playlist_id: "playlist_workout_2024" ,
artist_id: "artist_123" ,
genre: "electronic" ,
is_explicit: false ,
},
});
// Find songs in specific playlist
const songs = await query ({
question: "List all upbeat songs" ,
scope_filters: {
playlist_id: "playlist_workout_2024" ,
is_explicit: false ,
},
});
Version Control System
// Upload documentation for specific version
await uploadWithScopes ({
file: docsFile ,
scope_values: {
version: 2.1 ,
branch: "main" ,
environment: "production" ,
is_latest: true ,
},
});
// Query specific version docs
const answer = await query ({
question: "How do I configure the cache?" ,
scope_filters: {
version: 2.1 ,
environment: "production" ,
is_latest: true ,
},
});
Advanced Patterns
Hierarchical Scopes
Combine multiple scopes for fine-grained control:
{
"organization_id" : "org_abc" ,
"team_id" : "team_engineering" ,
"project_id" : "proj_backend" ,
"environment" : "staging"
}
Dynamic Scopes
Change scope values based on user context:
function getScopeFilters ( user ) {
return {
tenant_id: user . tenantId ,
workspace_id: user . activeWorkspace ,
access_level: user . role ,
};
}
const answer = await query ({
question: userQuestion ,
scope_filters: getScopeFilters ( currentUser ),
});
Scope Inheritance
Parent chat scopes are automatically inherited by subchats in V1 auth:
// Parent chat has scopes: project_id, team_id
// User subchat inherits these scopes automatically
// User can add additional scopes like user_preferences
Index Performance Scopes are indexed in Neo4j for fast filtering
Query Optimization Use specific scope values instead of wildcards
Cardinality Lower cardinality scopes perform better
Combination Limits Limit to 3-5 scope filters per query
// Fast: Specific values
scope_filters : {
playlist_id : "playlist_123" ,
is_public : true
}
// Slower: Many combinations
scope_filters : {
tag1 : "value1" ,
tag2 : "value2" ,
tag3 : "value3" ,
tag4 : "value4" ,
tag5 : "value5"
}
Best Practices
Design your scope structure before uploading documents. Consider:
What entities do you need to filter by?
What’s the cardinality of each scope?
Which scopes will be queried together?
Use Required Scopes Wisely
Only mark scopes as required if they’re truly essential. This provides
flexibility for different document types.
Use consistent naming conventions across your application: - snake_case for
scope names - Descriptive but concise names - Avoid abbreviations
Maintain documentation of what each scope means and when to use it.
Limitations
Current Limitations : - Maximum 20 scopes per chat - String values limited
to 255 characters - Scope names limited to 64 characters - No wildcard
matching (must use exact values)
Migration Guide
Adding Scopes to Existing Chat
Configure Scopes - Define your scope schema
Set as Optional - Make scopes optional initially
Re-upload Documents - Re-upload documents with scope values
Verify Filtering - Test queries with scope filters
Make Required - Optionally make scopes required for new uploads
// Step 1: Configure scopes
await configureScopes ({
scopes: [{ name: "project_id" , type: "string" , required: false }],
});
// Step 2: Re-upload with scopes
for ( const doc of existingDocs ) {
await uploadWithScopes ({
file: doc . file ,
scope_values: { project_id: doc . projectId },
});
}
// Step 3: Update to required
await configureScopes ({
scopes: [{ name: "project_id" , type: "string" , required: true }],
});
Error Handling
Invalid scope configuration or values {
"detail" : "Invalid scope name: user@email. Must contain only alphanumeric, underscores, hyphens."
}
Required scope missing {
"detail" : "Required scope 'playlist_id' is missing"
}
Type mismatch {
"detail" : "Scope 'version' expects number, got string"
}