Skip to main content

Exposure Profiles

Exposure Profiles define what data a user shares with apps. They're the key to user-controlled privacy.

What is an Exposure Profile?

An exposure profile is a set of rules that specify:

  • What types of nodes are accessible
  • Which tags are included or excluded
  • What permissions are granted (read, write, delete)

Think of it as a privacy filter between user data and third-party apps.

How It Works

┌─────────────────────────────────────────────────────────────────┐
│ USER'S DATA VAULT │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ All Data: Notes, Files, Contacts, Personal, Health... │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ Exposure Profile Filter │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Filtered: Only Work notes, Only tag "public" │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────┐
│ Third-Party │
│ App │
└─────────────────┘

Profile Structure

{
"id": "profile_work123",
"name": "Work Data Only",
"description": "Share only work-related notes and documents",
"owner_id": "user_123",
"scope": {
"node_types": ["note", "document"],
"tag_ids": ["tag_work", "tag_projects"],
"exclude_tag_ids": ["tag_private", "tag_personal"],
"permissions": ["read"]
},
"is_default": false,
"created_at": "2024-01-15T10:30:00Z"
}

Scope Options

FieldTypeDescription
node_typesarrayOnly these node types are visible
tag_idsarrayOnly nodes with these tags
exclude_tag_idsarrayExclude nodes with these tags
permissionsarrayread, write, delete

Permission Levels

PermissionAllows
readView nodes, list, search
writeCreate new nodes, update existing
deleteRemove nodes

Working with Exposure Profiles

Create a Profile

profile = client.exposure_profiles.create(
user_id="user-123",
name="Work Data Only",
description="Share work notes and documents",
scope={
"node_types": ["note", "document"],
"tag_ids": ["tag_work"],
"exclude_tag_ids": ["tag_private"],
"permissions": ["read"]
}
)

List Profiles

result = client.exposure_profiles.list(user_id="user-123")

for profile in result['items']:
print(f"📋 {profile['name']}")
print(f" Types: {profile['scope'].get('node_types', 'all')}")
print(f" Permissions: {profile['scope'].get('permissions', [])}")

Update a Profile

updated = client.exposure_profiles.update(
user_id="user-123",
profile_id="profile_work123",
scope={
"node_types": ["note", "document", "task"],
"permissions": ["read", "write"]
}
)

Delete a Profile

client.exposure_profiles.delete(
user_id="user-123",
profile_id="profile_work123"
)

Common Profile Patterns

Read-Only Work Data

{
"name": "Work Read-Only",
"scope": {
"tag_ids": ["tag_work"],
"exclude_tag_ids": ["tag_confidential"],
"permissions": ["read"]
}
}

Notes Only (No Files)

{
"name": "Notes Only",
"scope": {
"node_types": ["note"],
"permissions": ["read"]
}
}

Full Access (Trusted App)

{
"name": "Full Access",
"scope": {
"permissions": ["read", "write", "delete"]
}
}

Journal App

{
"name": "Journal Data",
"scope": {
"node_types": ["memory", "journal"],
"tag_ids": ["tag_journal", "tag_personal"],
"permissions": ["read", "write"]
}
}

Using Profiles for Sharing

When a user authorizes your app, they select an exposure profile:

# Create a share using a profile
share = client.sharing.create(
user_id="user-123",
third_party_id="your-app-id",
exposure_profile_id="profile_work123"
)

Your app can only access data matching that profile's scope.

Default Profile

Users can set a default profile for quick authorization:

client.exposure_profiles.update(
user_id="user-123",
profile_id="profile_work123",
is_default=True
)

Best Practices

1. Request Minimum Permissions

Only ask for what you need:

# Good - specific and minimal
{
"node_types": ["note"],
"permissions": ["read"]
}

# Bad - overly broad
{
"permissions": ["read", "write", "delete"]
}

2. Explain What You Need

When users authorize your app, clearly explain why you need each permission.

3. Respect the Scope

Even if you have the API key, only access what the exposure profile allows. Attempting to access out-of-scope data will return 403 errors.

4. Support Multiple Profiles

Let users choose different profiles for different use cases:

# User might have:
# - "Work Notes" for a work productivity app
# - "Health Data" for a health tracking app
# - "Full Journal" for their journaling app

How Apps See Filtered Data

When your app calls the API with a user who has granted access via a profile:

# App calls list nodes
nodes = client.nodes.list(user_id="user-123")

# Only returns nodes matching the exposure profile:
# - Matching node_types
# - Having required tags
# - Not having excluded tags

The API automatically filters based on the active exposure profile.

Next Steps