The Problem
Consider a SaaS application with an AI assistant. You need to handle several competing requirements:- User A should not see User B’s personal memories
- Users within the same organization should share some common context (e.g., company policies, project details)
- Your application has global knowledge that all users should benefit from (e.g., product documentation, feature capabilities)
- All of this needs to work without manual access control lists or complex permission logic
The Scope Hierarchy
Synap organizes memories into four nested scopes. Each scope is a superset of the one above it:
The scope hierarchy: USER is the narrowest (most isolated), WORLD is the broadest (shared across all instances).
How Scope Isolation Works
When you ingest a memory, Synap assigns it to a scope based on the identifiers you provide:- Pass
user_id: memory is stored at USER scope - Pass
customer_id(withoutuser_id): memory is stored at CUSTOMER scope - Pass neither: memory is stored at CLIENT scope
customer_id was set at ingestion time and stored server-side. On retrieval, Synap looks up that association and walks the chain for you. You can also pass customer_id explicitly on sdk.user.context.fetch(user_id=..., customer_id=...) for B2B instances when you want to assert the customer association at query time.
Setting Up User Scope
User scope is the most common isolation boundary. Every time you ingest a memory that belongs to a specific individual, pass theiruser_id.
ContextResponse.facts returns a merged, ranked list: the server has already walked the USER → CUSTOMER → CLIENT chain for you. If you need per-fact scope attribution (e.g., to render USER-scoped memories with different visual treatment), call sdk.fetch(...) instead and read UnifiedContextResponse.scope_map on the cross-scope unified response.
Setting Up Customer Scope
Customer scope represents an organization, team, or account. Memories at this scope are shared across all users within that customer.cust_acme will see this memory in their context:
Setting Up Client Scope
Client scope represents your entire application. Memories at this scope are visible to every user across all customers.Example: SaaS Project Management Tool
Let’s walk through a complete example. You are building an AI assistant for a project management tool. The assistant helps team members with tasks, deadlines, and project context.Defining Your Scope Strategy
Ingestion Code
Retrieval Code
Example: Consumer Mobile App
For simpler consumer applications without an organization concept, the scoping model is straightforward.Defining Your Scope Strategy
Ingestion Code
Retrieval Code
Primary scope: what Synap optimizes for
Each Instance has a primary scope: the level Synap optimizes indexing, caching, and retrieval for. It is chosen automatically based on your use-case file:Testing Scoped Access
When developing, verify that scope isolation works correctly by testing cross-scope access patterns:Scope Decision Flowchart
Use this flowchart to decide which scope identifiers to pass when ingesting memories:Is this memory about a specific individual?
user_id (and customer_id if the user belongs to an organization).No. Continue to the next question.Is this memory about a specific organization or team?
customer_id only (no user_id).No. Continue to the next question.Is this memory about your application or product?
user_id nor customer_id. It will be stored at client scope.No. This is likely general knowledge. Store at client scope or consider whether it should be ingested at all.Best Practices
Use consistent ID formats
Use consistent ID formats
user_id and customer_id values and enforce it across your application. Inconsistent IDs (e.g., "alice" vs "user_alice" vs "user-alice") create fragmented memory silos.Recommended: prefix-based IDs like user_<your_internal_id> and cust_<your_internal_id>.Always pass customer_id when you have it
Always pass customer_id when you have it
customer_id alongside user_id during ingestion and retrieval. This ensures customer-scoped memories are properly accessible and the scope hierarchy works correctly.Test with multiple users early
Test with multiple users early