Skip to main content

OfSelf Python SDK

Official Python SDK for the OfSelf API - Personal data sovereignty platform.

PyPI version Python versions License: MIT

Installation

pip install ofself

Quick Start

from ofself import OfSelfClient
from ofself.exceptions import NotFoundError, PermissionDenied

# Initialize the client with your API key
client = OfSelfClient(api_key="your-api-key")

# Create a node for a user
node = client.nodes.create(
    user_id="user-123",
    title="Meeting Notes",
    value="Discussed project timeline and milestones.",
    node_type="note",
    tag_ids=["tag-work"]
)
print(f"Created node: {node['id']}")

# List nodes
nodes = client.nodes.list(
    user_id="user-123",
    node_type="note",
    per_page=10
)
print(f"Found {nodes['total']} notes")

# Handle errors gracefully
try:
    node = client.nodes.get(user_id="user-123", node_id="invalid-id")
except NotFoundError:
    print("Node not found")
except PermissionDenied:
    print("User hasn't granted access")

Features

  • Full API coverage: Nodes, tags, files, relationships, proposals, and more
  • Type hints: Full typing support with IDE autocomplete
  • Error handling: Custom exceptions for different error types
  • Automatic retries: Built-in retry logic for transient failures
  • Context manager: Clean resource management

API Resources

Nodes

# Create a node
node = client.nodes.create(
    user_id="user-123",
    title="My Note",
    value="Note content",
    node_type="note",
    node_metadata={"source": "sdk"},
    tag_ids=["tag-1", "tag-2"]
)

# List nodes with filters
nodes = client.nodes.list(
    user_id="user-123",
    node_type="note",
    tag_ids=["tag-work"],
    search="meeting",
    page=1,
    per_page=50
)

# Get a specific node
node = client.nodes.get(user_id="user-123", node_id="node-456")

# Update a node
node = client.nodes.update(
    user_id="user-123",
    node_id="node-456",
    title="Updated Title",
    value="Updated content"
)

# Delete a node
client.nodes.delete(user_id="user-123", node_id="node-456")

# Add/remove tags
client.nodes.add_tag(user_id="user-123", node_id="node-456", tag_id="tag-work")
client.nodes.remove_tag(user_id="user-123", node_id="node-456", tag_id="tag-old")

Tags

# Create a tag
tag = client.tags.create(
    user_id="user-123",
    name="Work",
    color="#4A90D9"
)

# List tags
tags = client.tags.list(user_id="user-123")

# Get nodes with a tag
nodes = client.tags.get_nodes(user_id="user-123", tag_id="tag-work")

# Get files with a tag
files = client.tags.get_files(user_id="user-123", tag_id="tag-work")

Files

# Upload a file
file = client.files.upload(
    user_id="user-123",
    file="/path/to/document.pdf",
    tag_ids=["tag-documents"]
)

# Upload from file object
with open("document.pdf", "rb") as f:
    file = client.files.upload(
        user_id="user-123",
        file=f,
        filename="my-document.pdf"
    )

# List files
files = client.files.list(
    user_id="user-123",
    content_type="application/pdf"
)

# Download file content
content = client.files.download(user_id="user-123", file_id="file-456")
with open("downloaded.pdf", "wb") as f:
    f.write(content)

# Delete a file
client.files.delete(user_id="user-123", file_id="file-456")

Relationships

# Create a relationship
rel = client.relationships.create(
    user_id="user-123",
    source_node_id="node-a",
    target_node_id="node-b",
    relationship_type="references",
    weight=0.8
)

# List relationships
rels = client.relationships.list(
    user_id="user-123",
    node_id="node-a"  # Get all relationships involving this node
)

# Delete a relationship
client.relationships.delete(user_id="user-123", relationship_id="rel-123")

Proposals

Third-party apps propose changes; users approve or reject.

# Create a proposal to add nodes
proposal = client.proposals.create(
    user_id="user-123",
    title="Extract Entities from Chunk 0",
    type="CREATE_NODE",
    canonical_data={
        "entities": [
            {
                "title": "Imported Note",
                "value": "Content from external app",
                "node_type": "note",
                "tags": ["imported"]
            }
        ]
    },
    raw_data={"source": "my-app", "original_id": "ext-123"}
)

# List pending proposals
proposals = client.proposals.list(
    user_id="user-123",
    status="pending"
)

# Approve a proposal
result = client.proposals.approve(
    user_id="user-123",
    proposal_id="prop-456"
)

# Reject a proposal
client.proposals.reject(
    user_id="user-123",
    proposal_id="prop-789",
    reason="Duplicate data"
)

Exposure Profiles

Control what data is shared with apps.

# Create an exposure profile
profile = client.exposure_profiles.create(
    user_id="user-123",
    name="Work Data Only",
    description="Share only work-related nodes",
    scope={
        "node_types": ["note", "document"],
        "tag_ids": ["tag-work"],
        "permissions": ["read"],
        "exclude_tag_ids": ["tag-private"]
    }
)

# List profiles
profiles = client.exposure_profiles.list(user_id="user-123")

Sharing

# Share data with a third-party app
share = client.sharing.create(
    user_id="user-123",
    third_party_id="app-456",
    exposure_profile_id="profile-789"
)

# List outgoing shares
shares = client.sharing.list_outgoing(user_id="user-123")

# Revoke a share
client.sharing.revoke(user_id="user-123", share_id="share-abc")

Error Handling

The SDK raises specific exceptions for different error types:

from ofself.exceptions import (
    OfSelfError,           # Base exception
    AuthenticationError,   # Invalid/missing API key (401)
    PermissionDenied,      # Access denied (403)
    NotFoundError,         # Resource not found (404)
    ValidationError,       # Invalid request data (400/422)
    RateLimitError,        # Rate limit exceeded (429)
    ServerError,           # Server error (5xx)
    ConnectionError,       # Network issues
)

try:
    node = client.nodes.get(user_id="user-123", node_id="invalid")
except NotFoundError as e:
    print(f"Not found: {e.message}")
    print(f"Status code: {e.status_code}")
except PermissionDenied as e:
    print("User hasn't granted access to this node")
except RateLimitError as e:
    print(f"Rate limited. Retry after {e.retry_after} seconds")
except OfSelfError as e:
    print(f"API error: {e}")

Configuration

# Custom configuration
client = OfSelfClient(
    api_key="your-api-key",
    base_url="https://api.ofself.ai/api/v1",  # Default
    timeout=30.0,  # Request timeout in seconds
)

# Use as context manager for automatic cleanup
with OfSelfClient(api_key="your-api-key") as client:
    nodes = client.nodes.list(user_id="user-123")

Development

# Clone the repository
git clone https://github.com/ofself/ofself-sdk-python.git
cd ofself-sdk-python

# Install development dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Run linting
black src/ tests/
isort src/ tests/
mypy src/

# Run all checks
pytest && black --check src/ && mypy src/

License

MIT License - see LICENSE for details.

Metadata

Release files for ofself 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ofself 0.1.0
File Size Uploaded
ofself-0.1.0.tar.gz 22.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ofself 0.1.0
File Interpreter ABI Platform
ofself-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 55.8 kB

Release files / ofself-0.1.0.tar.gz

Download URL ofself-0.1.0.tar.gz
Size 22.1 kB
Tags Source
SHA-256 checksum
How to use checksums
0c9303763b3baf66eb88c35901d7d405cae8daa123a11fe0e93e86932850e9b5
BLAKE2b-256 checksum
How to use checksums
56fc040f419cd0bc49ee7f53e1a3d7465bd33f2c86c3d00658d08e33425ca392
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.11

Release files / ofself-0.1.0-py3-none-any.whl

Download URL ofself-0.1.0-py3-none-any.whl
Size 33.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
59c805eac27c7df7bed3d5d6f2ab38673a9a2b483d864e169bca0b2b61719f5e
BLAKE2b-256 checksum
How to use checksums
e345abebf98fc43da8a3f2590693806bf1eb7367d854ad51ea2a41ccaacc3437
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.11

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page