Metadata-Version: 2.4
Name: velt-py
Version: 0.1.10
Summary: Python SDK for integrating Velt comments, reactions, attachments, and user management into Django applications
Home-page: https://github.com/snippyly/velt-py-sdk
Author: Velt
Author-email: Velt <support@velt.dev>
License: MIT
Project-URL: Homepage, https://github.com/snippyly/velt-py-sdk
Project-URL: Repository, https://github.com/snippyly/velt-py-sdk
Keywords: velt,comments,collaboration,django,mongodb
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Framework :: Django
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pymongo[srv,tls]>=4.6.3
Requires-Dist: mongoengine>=0.27.0
Requires-Dist: blinker>=1.8.2
Requires-Dist: python-dateutil>=2.8.2
Requires-Dist: requests>=2.25.0
Requires-Dist: boto3>=1.28.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: mongomock>=4.1.2; extra == "dev"
Requires-Dist: responses>=0.23.0; extra == "dev"
Dynamic: author
Dynamic: home-page
Dynamic: license-file
Dynamic: requires-python

# Velt Python SDK

A Python SDK for integrating Velt collaboration features into Python applications. Supports two backends:

- **Self-Hosting** (`sdk.selfHosting.*`) — Direct MongoDB access for self-hosted Velt installations
- **REST API** (`sdk.api.*`) — Wrappers around Velt's REST APIs at `api.velt.dev`

## Installation

```bash
pip install velt-py
```

## Requirements

- Python 3.8+
- Django 4.2.26+
- MongoDB (Percona Server or MongoDB Atlas)
- PyMongo 4.6.3
- MongoEngine 0.27.0

## Quick Start

### 1. Initialize the SDK

```python
from velt_py import VeltSDK

# Initialize SDK with your MongoDB configuration
sdk = VeltSDK.initialize(config={
    'database': {
        'host': 'localhost:27017',
        'username': 'your-username',
        'password': 'your-password',
        'auth_database': 'admin',
        'database_name': 'velt-integration'  # Your database name
    },
    'apiKey': 'your-velt-api-key',  # Optional: can use VELT_API_KEY env var
    'authToken': 'your-velt-auth-token'  # Optional: can use VELT_AUTH_TOKEN env var
})
```

### 2. Use Services

The SDK supports different backends. For self-hosted installations, use the `selfHosting` backend:

```python
# Comments
result = sdk.selfHosting.comments.getComments(
    organizationId='org-123',
    commentAnnotationIds=['ann-1', 'ann-2'],
    documentIds=['doc-1']
)

sdk.selfHosting.comments.saveComments(
    organizationId='org-123',
    commentAnnotation={
        'ann-1': {
            'comments': {'comment-1': {'commentId': 'comment-1', 'commentText': 'Hello'}},
            'metadata': {}
        }
    },
    documentId='doc-1'
)

sdk.selfHosting.comments.deleteComment(
    organizationId='org-123',
    commentAnnotationId='ann-1'
)

# Reactions
result = sdk.selfHosting.reactions.getReactions(
    organizationId='org-123',
    reactionAnnotationIds=['reaction-1'],
    documentIds=['doc-1']
)

sdk.selfHosting.reactions.saveReactions(
    organizationId='org-123',
    reactionAnnotation={
        'reaction-1': {
            'icon': '👍',
            'metadata': {}
        }
    },
    documentId='doc-1'
)

# Attachments
result = sdk.selfHosting.attachments.getAttachment(
    organizationId='org-123',
    attachmentId=12345
)

sdk.selfHosting.attachments.saveAttachment(
    organizationId='org-123',
    attachment={
        'attachmentId': 12345,
        'name': 'document.pdf',
        'mimeType': 'application/pdf',
        'base64Data': '...',
        'size': 1024
    },
    documentId='doc-1'
)

# Users
result = sdk.selfHosting.users.getUsers(
    organizationId='org-123',
    userIds=['user-1', 'user-2']
)

sdk.selfHosting.users.saveUser(
    organizationId='org-123',
    user={
        'userId': 'user-1',
        'name': 'John Doe',
        'email': 'john@example.com',
        'photoUrl': 'https://example.com/photo.jpg'
    }
)

# Token
result = sdk.selfHosting.token.getToken(
    organizationId='org-123',
    userId='user-1',
    email='john@example.com',
    isAdmin=False
)
token = result['data']['token']
```

### 3. REST API Services

For calling Velt's REST APIs (no self-hosted database needed), use the `api` backend. Each method takes a typed request object:

```python
from velt_py.models.organization import (
    AddOrganizationsRequest,
    GetOrganizationsRequest,
    UpdateOrganizationsRequest,
    DeleteOrganizationsRequest,
    UpdateOrganizationDisableStateRequest
)
from velt_py.models.folder import (
    AddFolderRequest,
    GetFoldersRequest,
    UpdateFolderRequest,
    DeleteFolderRequest,
    UpdateFolderAccessRequest
)
from velt_py.models.notification_api import (
    AddNotificationsRequest,
    GetNotificationsRequest,
    SetNotificationConfigRequest
)
from velt_py.models.user_api import (
    AddUsersRequest,
    GetUsersRequest,
    UpdateUsersRequest,
    DeleteUsersRequest
)
from velt_py.models.user_group import (
    AddUserGroupsRequest,
    AddUsersToGroupRequest,
    DeleteUsersFromGroupRequest
)
from velt_py.models.document import (
    AddDocumentsRequest,
    GetDocumentsRequest,
    UpdateDocumentsRequest
)
from velt_py.models.activity_api import (
    AddActivitiesRequest,
    GetActivitiesRequest,
    UpdateActivitiesRequest,
    DeleteActivitiesRequest
)
from velt_py.models.comment_annotation_api import (
    AddCommentAnnotationsRequest,
    GetCommentAnnotationsRequest,
    GetCommentAnnotationsCountRequest,
    UpdateCommentAnnotationsRequest,
    DeleteCommentAnnotationsRequest,
    AddCommentsRequest,
    GetCommentsRequest,
    UpdateCommentsRequest,
    DeleteCommentsRequest
)
from velt_py.models.crdt import (
    AddCrdtDataRequest,
    GetCrdtDataRequest,
    UpdateCrdtDataRequest
)
from velt_py.models.gdpr import (
    DeleteAllUserDataRequest,
    GetAllUserDataRequest,
    GetDeleteUserDataStatusRequest
)
from velt_py.models.access_control import (
    AddPermissionsRequest,
    GetPermissionsRequest,
    RemovePermissionsRequest,
    GenerateSignatureRequest,
    GenerateTokenRequest
)
from velt_py.models.presence import (
    AddPresenceRequest,
    UpdatePresenceRequest,
    DeletePresenceRequest
)
from velt_py.models.livestate import (
    BroadcastEventRequest
)
from velt_py.models.recording import (
    GetRecordingsRequest
)
from velt_py.models.rewriter import (
    AskAiRequest
)
from velt_py.models.workspace import (
    CreateWorkspaceRequest,
    GetWorkspaceRequest,
    CreateApiKeyRequest,
    UpdateApiKeyRequest,
    GetApiKeysRequest,
    GetApiKeyMetadataRequest,
    ResetAuthTokenRequest,
    GetAuthTokensRequest,
    AddDomainsRequest,
    DeleteDomainsRequest,
    GetDomainsRequest,
    GetEmailStatusRequest,
    SendLoginLinkRequest,
    GetEmailConfigRequest,
    UpdateEmailConfigRequest,
    GetWebhookConfigRequest,
    UpdateWebhookConfigRequest
)

# Organizations — Add
result = sdk.api.organizations.addOrganizations(
    AddOrganizationsRequest(
        organizations=[{'organizationId': 'org-123', 'organizationName': 'My Organization'}]
    )
)

# Organizations — Get (with optional filters and pagination)
result = sdk.api.organizations.getOrganizations(
    GetOrganizationsRequest(
        organizationIds=['org-123'],  # optional, omit to get all
        pageSize=10,                  # optional
        pageToken='next-page-token'   # optional
    )
)

# Organizations — Update
result = sdk.api.organizations.updateOrganizations(
    UpdateOrganizationsRequest(
        organizations=[{'organizationId': 'org-123', 'organizationName': 'Updated Name'}]
    )
)

# Organizations — Delete
result = sdk.api.organizations.deleteOrganizations(
    DeleteOrganizationsRequest(organizationIds=['org-123', 'org-456'])
)

# Organizations — Disable/Enable access
result = sdk.api.organizations.updateOrganizationDisableState(
    UpdateOrganizationDisableStateRequest(organizationIds=['org-123'], disabled=True)
)

# Notifications — Add
result = sdk.api.notifications.addNotifications(
    AddNotificationsRequest(
        organizationId='org-123',
        documentId='doc-1',
        actionUser={'userId': 'user-1', 'name': 'John Doe', 'email': 'john@example.com'},
        displayHeadlineMessageTemplate='{actionUser} commented on your document',
        displayBodyMessage='Check out the new comment',
        notifyUsers=[{'userId': 'user-2', 'name': 'Jane Doe'}],
        createOrganization=True,   # optional
        createDocument=True         # optional
    )
)

# Notifications — Get (with filters and pagination)
result = sdk.api.notifications.getNotifications(
    GetNotificationsRequest(
        organizationId='org-123',
        userId='user-2',          # or documentId
        pageSize=10,              # optional
        order='desc'              # optional: asc or desc
    )
)

# Notifications — Set config (notification preferences)
result = sdk.api.notifications.setNotificationConfig(
    SetNotificationConfigRequest(
        organizationId='org-123',
        userIds=['user-1', 'user-2'],
        config={'inbox': 'ALL', 'email': 'MINE', 'slack': 'NONE'}
    )
)

# Users — Add to organization
result = sdk.api.users.addUsers(
    AddUsersRequest(
        organizationId='org-123',
        users=[{'userId': 'user-1', 'name': 'John Doe', 'email': 'john@example.com', 'accessRole': 'editor'}],
        createOrganization=True  # optional: creates org if it doesn't exist
    )
)

# Users — Get by organization (with optional filters and pagination)
result = sdk.api.users.getUsers(
    GetUsersRequest(
        organizationId='org-123',
        userIds=['user-1'],         # optional
        documentId='doc-1',         # optional
        pageSize=100,               # optional
        allDocuments=True,          # optional: get all document-level users
        groupByDocumentId=True      # optional: group results by document
    )
)

# Users — Update metadata
result = sdk.api.users.updateUsers(
    UpdateUsersRequest(
        organizationId='org-123',
        users=[{'userId': 'user-1', 'name': 'Jane Doe', 'accessRole': 'viewer'}]
    )
)

# Users — Delete from organization
result = sdk.api.users.deleteUsers(
    DeleteUsersRequest(
        organizationId='org-123',
        userIds=['user-1', 'user-2']
    )
)

# User Groups — Add groups
result = sdk.api.userGroups.addUserGroups(
    AddUserGroupsRequest(
        organizationId='org-123',
        organizationUserGroups=[{'groupId': 'engineering', 'groupName': 'Engineering'}],
        createOrganization=True  # optional
    )
)

# User Groups — Add users to a group
result = sdk.api.userGroups.addUsersToGroup(
    AddUsersToGroupRequest(
        organizationId='org-123',
        organizationUserGroupId='engineering',
        userIds=['user-1', 'user-2']
    )
)

# User Groups — Remove users from a group
result = sdk.api.userGroups.deleteUsersFromGroup(
    DeleteUsersFromGroupRequest(
        organizationId='org-123',
        organizationUserGroupId='engineering',
        userIds=['user-1'],    # specific users
        # deleteAll=True       # or remove all users from group
    )
)

# Documents — Add
result = sdk.api.documents.addDocuments(
    AddDocumentsRequest(
        organizationId='org-123',
        documents=[
            {'documentId': 'doc-1', 'documentName': 'My Document'},
            {'documentId': 'doc-2', 'documentName': 'Another Document'}
        ],
        createOrganization=True,  # optional: creates org if it doesn't exist
        folderId='folder-1',      # optional: add to a specific folder
        createFolder=True          # optional: creates folder if it doesn't exist
    )
)

# Documents — Get (with optional filters and pagination)
result = sdk.api.documents.getDocuments(
    GetDocumentsRequest(
        organizationId='org-123',
        documentIds=['doc-1'],    # optional
        folderId='folder-1',      # optional
        pageSize=10               # optional
    )
)

# Documents — Update
result = sdk.api.documents.updateDocuments(
    UpdateDocumentsRequest(
        organizationId='org-123',
        documents=[{'documentId': 'doc-1', 'documentName': 'Updated Name'}]
    )
)

# Documents — Delete
result = sdk.api.documents.deleteDocuments(
    DeleteDocumentsRequest(
        organizationId='org-123',
        documentIds=['doc-1', 'doc-2']
    )
)

# Documents — Move to a different folder
result = sdk.api.documents.moveDocuments(
    MoveDocumentsRequest(
        organizationId='org-123',
        documentIds=['doc-1', 'doc-2'],
        folderId='target-folder-id'
    )
)

# Documents — Update access type
result = sdk.api.documents.updateDocumentAccess(
    UpdateDocumentAccessRequest(
        organizationId='org-123',
        documentIds=['doc-1', 'doc-2'],
        accessType='organizationPrivate'  # 'organizationPrivate', 'restricted', or 'public'
    )
)

# Documents — Disable/enable access
result = sdk.api.documents.updateDocumentDisableState(
    UpdateDocumentDisableStateRequest(
        organizationId='org-123',
        documentIds=['doc-1'],
        disabled=True
    )
)

# Documents — Migrate to a new document ID
result = sdk.api.documents.migrateDocuments(
    MigrateDocumentsRequest(
        organizationId='org-123',
        documentId='old-doc-id',
        newDocumentId='new-doc-id'
    )
)

# Documents — Poll migration status
result = sdk.api.documents.migrateDocumentsStatus(
    MigrateDocumentsStatusRequest(
        organizationId='org-123',
        migrationId='migration-id-from-migrate-call'
    )
)

# Folders — Add
result = sdk.api.folders.addFolder(
    AddFolderRequest(
        organizationId='org-123',
        folders=[{'folderId': 'folder-1', 'folderName': 'My Folder'}],
        createOrganization=True  # optional
    )
)

# Folders — Get (with optional folderId and maxDepth)
result = sdk.api.folders.getFolders(
    GetFoldersRequest(organizationId='org-123', folderId='folder-1', maxDepth=3)
)

# Folders — Update
result = sdk.api.folders.updateFolder(
    UpdateFolderRequest(
        organizationId='org-123',
        folders=[{'folderId': 'folder-1', 'folderName': 'Renamed Folder'}]
    )
)

# Folders — Delete
result = sdk.api.folders.deleteFolder(
    DeleteFolderRequest(organizationId='org-123', folderId='folder-1')
)

# Folders — Update access type
result = sdk.api.folders.updateFolderAccess(
    UpdateFolderAccessRequest(
        organizationId='org-123',
        folderIds=['folder-1'],
        accessType='restricted'
    )
)

# Activities — Add activity logs
result = sdk.api.activities.addActivities(
    AddActivitiesRequest(
        organizationId='org-123',
        documentId='doc-456',
        activities=[{
            'featureType': 'comment',
            'actionType': 'comment.add',
            'actionUser': {'userId': 'user-1', 'name': 'John Doe', 'email': 'john@example.com'},
            'displayMessageTemplate': '{{user}} added a comment'
        }]
    )
)

# Activities — Get (with filters and pagination)
result = sdk.api.activities.getActivities(
    GetActivitiesRequest(
        organizationId='org-123',
        documentId='doc-456',
        featureTypes=['comment'],
        order='desc',
        pageSize=50
    )
)

# Activities — Update
result = sdk.api.activities.updateActivities(
    UpdateActivitiesRequest(
        organizationId='org-123',
        activities=[{
            'id': 'activity-001',
            'displayMessageTemplate': '{{user}} updated the comment'
        }]
    )
)

# Activities — Delete by document
result = sdk.api.activities.deleteActivities(
    DeleteActivitiesRequest(
        organizationId='org-123',
        documentId='doc-456'
    )
)

# Comment Annotations — Add
result = sdk.api.commentAnnotations.addCommentAnnotations(
    AddCommentAnnotationsRequest(
        organizationId='org-123',
        documentId='doc-1',
        commentAnnotations=[{
            'location': {'id': 'section-1', 'locationName': 'Introduction'},
            'commentData': [{
                'commentText': 'This needs review',
                'commentHtml': '<p>This needs review</p>',
                'from': {'userId': 'user-1', 'name': 'John Doe', 'email': 'john@example.com'}
            }]
        }]
    )
)

# Comment Annotations — Get (with filters and pagination)
result = sdk.api.commentAnnotations.getCommentAnnotations(
    GetCommentAnnotationsRequest(
        organizationId='org-123',
        documentId='doc-1',
        statusIds=['OPEN'],      # optional
        pageSize=10              # optional
    )
)

# Comment Annotations — Get count (total and unread per document)
result = sdk.api.commentAnnotations.getCommentAnnotationsCount(
    GetCommentAnnotationsCountRequest(
        organizationId='org-123',
        documentIds=['doc-1', 'doc-2'],
        userId='user-1'
    )
)

# Comment Annotations — Update (e.g., change status)
result = sdk.api.commentAnnotations.updateCommentAnnotations(
    UpdateCommentAnnotationsRequest(
        organizationId='org-123',
        documentId='doc-1',
        annotationIds=['annotation-id-1'],
        updatedData={'status': {'id': 'RESOLVED', 'name': 'Resolved', 'type': 'terminal'}}
    )
)

# Comment Annotations — Delete
result = sdk.api.commentAnnotations.deleteCommentAnnotations(
    DeleteCommentAnnotationsRequest(
        organizationId='org-123',
        documentId='doc-1',
        annotationIds=['annotation-id-1']
    )
)

# Comments — Add to an existing annotation
result = sdk.api.commentAnnotations.addComments(
    AddCommentsRequest(
        organizationId='org-123',
        documentId='doc-1',
        annotationId='annotation-id-1',
        commentData=[{
            'commentText': 'I agree, let me fix this',
            'from': {'userId': 'user-2', 'name': 'Jane Doe'}
        }]
    )
)

# Comments — Get from an annotation
result = sdk.api.commentAnnotations.getComments(
    GetCommentsRequest(
        organizationId='org-123',
        documentId='doc-1',
        annotationId='annotation-id-1',
        userIds=['user-1']
    )
)

# Comments — Update
result = sdk.api.commentAnnotations.updateComments(
    UpdateCommentsRequest(
        organizationId='org-123',
        documentId='doc-1',
        annotationId='annotation-id-1',
        commentIds=[123456],
        updatedData={'commentText': 'Updated text', 'commentHtml': '<p>Updated text</p>'}
    )
)

# Comments — Delete
result = sdk.api.commentAnnotations.deleteComments(
    DeleteCommentsRequest(
        organizationId='org-123',
        documentId='doc-1',
        annotationId='annotation-id-1',
        commentIds=[123456]  # optional: omit to delete all
    )
)

# Access Control — Add permissions
result = sdk.api.accessControl.addPermissions(
    AddPermissionsRequest(
        user={'userId': 'user-1'},
        permissions={'resources': [
            {'type': 'organization', 'id': 'org-123', 'accessRole': 'editor'},
            {'type': 'document', 'id': 'doc-1', 'organizationId': 'org-123', 'accessRole': 'viewer', 'expiresAt': 1728902400}
        ]}
    )
)

# Access Control — Get permissions
result = sdk.api.accessControl.getPermissions(
    GetPermissionsRequest(
        organizationId='org-123',
        userIds=['user-1'],
        documentIds=['doc-1']  # optional
    )
)

# Access Control — Remove permissions
result = sdk.api.accessControl.removePermissions(
    RemovePermissionsRequest(
        userId='user-1',
        permissions={'resources': [
            {'type': 'organization', 'id': 'org-123'},
            {'type': 'document', 'id': 'doc-1', 'organizationId': 'org-123'}
        ]}
    )
)

# Access Control — Generate signature for Permission Provider
result = sdk.api.accessControl.generateSignature(
    GenerateSignatureRequest(
        permissions=[{
            'userId': 'user-1',
            'resourceId': 'doc-1',
            'type': 'document',
            'hasAccess': True,
            'accessRole': 'viewer'
        }]
    )
)

# Access Control — Generate JWT token
result = sdk.api.accessControl.generateToken(
    GenerateTokenRequest(
        userId='user-1',
        userProperties={'name': 'John Doe', 'email': 'john@example.com', 'isAdmin': False},
        permissions={'resources': [
            {'type': 'organization', 'id': 'org-123', 'accessRole': 'viewer'},
            {'type': 'document', 'id': 'doc-1', 'organizationId': 'org-123', 'accessRole': 'editor'}
        ]}
    )
)

# CRDT — Add editor data (text, map, array, or xml)
result = sdk.api.crdt.addCrdtData(
    AddCrdtDataRequest(
        organizationId='org-123',
        documentId='doc-1',
        editorId='my-collab-note',
        data='Hello, collaborative world!',
        type='text'
    )
)

# CRDT — Get editor data (with optional editorId filter)
result = sdk.api.crdt.getCrdtData(
    GetCrdtDataRequest(
        organizationId='org-123',
        documentId='doc-1',
        editorId='my-collab-note'  # optional: omit to get all editors
    )
)

# CRDT — Update existing editor data
result = sdk.api.crdt.updateCrdtData(
    UpdateCrdtDataRequest(
        organizationId='org-123',
        documentId='doc-1',
        editorId='my-collab-note',
        data='Updated collaborative content!',
        type='text'
    )
)

# Presence — Add users to presence
result = sdk.api.presence.addPresence(
    AddPresenceRequest(
        organizationId='org-123',
        documentId='doc-456',
        users=[
            {'userId': 'user-1', 'name': 'John Doe', 'email': 'john@example.com', 'status': 'online'},
            {'userId': 'user-2', 'name': 'Jane Doe', 'status': 'away'}
        ]
    )
)

# Presence — Update user presence status
result = sdk.api.presence.updatePresence(
    UpdatePresenceRequest(
        organizationId='org-123',
        documentId='doc-456',
        users=[{'userId': 'user-1', 'status': 'away'}]
    )
)

# Presence — Remove users from presence
result = sdk.api.presence.deletePresence(
    DeletePresenceRequest(
        organizationId='org-123',
        documentId='doc-456',
        userIds=['user-1', 'user-2']
    )
)

# Livestate — Broadcast event
result = sdk.api.livestate.broadcastEvent(
    BroadcastEventRequest(
        organizationId='org-123',
        documentId='doc-1',
        liveStateDataId='my-live-state',
        data={'status': 'active', 'message': 'Hello World'},
        merge=True  # optional: merge with existing data instead of replacing
    )
)

# Recordings — Get recordings for an organization/document
result = sdk.api.recordings.getRecordings(
    GetRecordingsRequest(
        organizationId='org-123',
        documentId='doc-456',         # optional
        recordingIds=['rec-1'],       # optional: filter by specific IDs
        pageSize=10,                  # optional
        pageToken='next-page-token'   # optional
    )
)

# Rewriter — Ask AI to rewrite/process text
result = sdk.api.rewriter.askAi(
    AskAiRequest(
        model='gpt-4o',
        prompt='Fix the grammar and spelling in the following text.',
        text='Their going to the store to by some apples.'
    )
)

# GDPR — Delete all user data
result = sdk.api.gdpr.deleteAllUserData(
    DeleteAllUserDataRequest(
        userIds=['user-1', 'user-2'],
        organizationIds=['org-123']  # optional, speeds up deletion
    )
)

# GDPR — Get all user data (paginated)
result = sdk.api.gdpr.getAllUserData(
    GetAllUserDataRequest(
        organizationId='org-123',
        userId='user-1',
        pageToken='next-page-token'  # optional
    )
)

# GDPR — Get deletion job status
result = sdk.api.gdpr.getDeleteUserDataStatus(
    GetDeleteUserDataStatusRequest(jobId='job-id-from-delete-call')
)

# Workspace — Create a new workspace
result = sdk.api.workspace.createWorkspace(
    CreateWorkspaceRequest(
        ownerEmail='owner@example.com',
        workspaceName='My Workspace',
        name='John Doe'
    )
)

# Workspace — Get workspace details
result = sdk.api.workspace.getWorkspace(GetWorkspaceRequest())

# Workspace — Create an API key
result = sdk.api.workspace.createApiKey(
    CreateApiKeyRequest(
        ownerEmail='owner@example.com',
        type='production',
        createAuthToken=True,
        apiKeyName='Production Key',
        allowedDomains=['example.com', '*.example.com']
    )
)

# Workspace — Update an API key name
result = sdk.api.workspace.updateApiKey(
    UpdateApiKeyRequest(apiKey='velt_api_key_1', apiKeyName='Renamed Key')
)

# Workspace — Get API keys (with optional pagination)
result = sdk.api.workspace.getApiKeys(
    GetApiKeysRequest(pageSize=50, pageToken='next-page-token')
)

# Workspace — Get API key metadata
result = sdk.api.workspace.getApiKeyMetadata(GetApiKeyMetadataRequest())

# Workspace — Reset auth token for an API key
result = sdk.api.workspace.resetAuthToken(
    ResetAuthTokenRequest(apiKey='velt_api_key_1')
)

# Workspace — Get auth tokens for an API key
result = sdk.api.workspace.getAuthTokens(
    GetAuthTokensRequest(apiKey='velt_api_key_1')
)

# Workspace — Add allowed domains
result = sdk.api.workspace.addDomains(
    AddDomainsRequest(domains=['example.com', '*.firebase.com'])
)

# Workspace — Delete allowed domains
result = sdk.api.workspace.deleteDomains(
    DeleteDomainsRequest(domains=['example.com'])
)

# Workspace — Get allowed domains
result = sdk.api.workspace.getDomains(GetDomainsRequest())

# Workspace — Check email verification status
result = sdk.api.workspace.getEmailStatus(
    GetEmailStatusRequest(ownerEmail='owner@example.com')
)

# Workspace — Send login link
result = sdk.api.workspace.sendLoginLink(
    SendLoginLinkRequest(email='user@example.com', continueUrl='https://app.example.com/dashboard')
)

# Workspace — Get email configuration
result = sdk.api.workspace.getEmailConfig(GetEmailConfigRequest())

# Workspace — Update email configuration
result = sdk.api.workspace.updateEmailConfig(
    UpdateEmailConfigRequest(
        useEmailService=True,
        emailServiceConfig={'type': 'sendgrid', 'apiKey': 'sg-key', 'fromEmail': 'noreply@example.com'}
    )
)

# Workspace — Get webhook configuration
result = sdk.api.workspace.getWebhookConfig(GetWebhookConfigRequest())

# Workspace — Update webhook configuration
result = sdk.api.workspace.updateWebhookConfig(
    UpdateWebhookConfigRequest(
        useWebhookService=True,
        webhookServiceConfig={
            'authToken': 'webhook-secret',
            'rawNotificationUrl': 'https://example.com/webhook/raw',
            'processedNotificationUrl': 'https://example.com/webhook/processed'
        }
    )
)

# Success response (raw from Velt API):
# {
#     'result': {
#         'status': 'success',
#         'message': 'Organization(s) added successfully.',
#         'data': {
#             'org-123': {
#                 'success': True,
#                 'id': '02cf91e5...',
#                 'message': 'Added Successfully'
#             }
#         }
#     }
# }

# Error response (raw from Velt API):
# {
#     'error': {
#         'message': 'Error adding organization(s).',
#         'status': 'INTERNAL',
#         'details': {
#             'org-123': {
#                 'success': False,
#                 'message': 'Organization already exists.'
#             }
#         }
#     }
# }
```

> **Note:** REST API service methods return the raw Velt API response as-is, preserving all fields. There is no local validation — the Velt API handles all validation server-side.

## Response Format

### Self-Hosting Services

Self-hosting service methods return a standardized response format:

### Success Response

```python
{
    'success': True,
    'data': {
        # Service-specific data
    }
}
```

### Error Response

```python
{
    'success': False,
    'error': 'Human-readable error message',
    'errorCode': 'ERROR_CODE'  # Optional
}
```

## Data Isolation

All methods require `organizationId` as the first parameter to ensure data isolation between organizations. The SDK automatically filters all queries by `organizationId`.

## API Reference

### REST API Services (`sdk.api.*`)

#### OrganizationService (`sdk.api.organizations`)

- `addOrganizations(request: AddOrganizationsRequest)` - Add one or more organizations.
- `getOrganizations(request: GetOrganizationsRequest)` - Get organizations. Optionally filter by IDs with pagination.
- `updateOrganizations(request: UpdateOrganizationsRequest)` - Update one or more organizations.
- `deleteOrganizations(request: DeleteOrganizationsRequest)` - Delete organizations by ID list.
- `updateOrganizationDisableState(request: UpdateOrganizationDisableStateRequest)` - Enable or disable access.

#### DocumentService (`sdk.api.documents`)

- `addDocuments(request: AddDocumentsRequest)` - Add documents to an organization.
- `getDocuments(request: GetDocumentsRequest)` - Get documents. Optionally filter by IDs, folder, with pagination.
- `updateDocuments(request: UpdateDocumentsRequest)` - Update document metadata.
- `deleteDocuments(request: DeleteDocumentsRequest)` - Delete documents by ID list.
- `moveDocuments(request: MoveDocumentsRequest)` - Move documents to a different folder.
- `updateDocumentAccess(request: UpdateDocumentAccessRequest)` - Update access type for documents.
- `updateDocumentDisableState(request: UpdateDocumentDisableStateRequest)` - Enable or disable document access.
- `migrateDocuments(request: MigrateDocumentsRequest)` - Migrate a document to a new document ID.
- `migrateDocumentsStatus(request: MigrateDocumentsStatusRequest)` - Poll migration status.

#### UserService (`sdk.api.users`)

- `addUsers(request: AddUsersRequest)` - Add users to an organization, document, or folder.
- `getUsers(request: GetUsersRequest)` - Get users with optional filters (document, folder, user IDs, groups) and pagination.
- `updateUsers(request: UpdateUsersRequest)` - Update user metadata in an organization, document, or folder.
- `deleteUsers(request: DeleteUsersRequest)` - Remove users from an organization, document, or folder.

#### NotificationApiService (`sdk.api.notifications`)

- `addNotifications(request: AddNotificationsRequest)` - Add notifications to users.
- `getNotifications(request: GetNotificationsRequest)` - Get notifications with optional filters and pagination.
- `updateNotifications(request: UpdateNotificationsRequest)` - Update existing notifications.
- `deleteNotifications(request: DeleteNotificationsRequest)` - Delete notifications.
- `getNotificationConfig(request: GetNotificationConfigRequest)` - Get notification preferences for a user.
- `setNotificationConfig(request: SetNotificationConfigRequest)` - Set notification preferences for users.

#### UserGroupService (`sdk.api.userGroups`)

- `addUserGroups(request: AddUserGroupsRequest)` - Add organization user groups.
- `addUsersToGroup(request: AddUsersToGroupRequest)` - Add users to a specific user group.
- `deleteUsersFromGroup(request: DeleteUsersFromGroupRequest)` - Remove users from a user group (specific users or all).

#### CommentAnnotationApiService (`sdk.api.commentAnnotations`)

- `addCommentAnnotations(request: AddCommentAnnotationsRequest)` - Add comment annotations to a document.
- `getCommentAnnotations(request: GetCommentAnnotationsRequest)` - Get comment annotations with optional filters (location, user, status, timestamps) and pagination.
- `getCommentAnnotationsCount(request: GetCommentAnnotationsCountRequest)` - Get total and unread comment annotation counts per document.
- `updateCommentAnnotations(request: UpdateCommentAnnotationsRequest)` - Update comment annotations (status, priority, assigned user, etc.).
- `deleteCommentAnnotations(request: DeleteCommentAnnotationsRequest)` - Delete comment annotations by document, location, annotation IDs, or user IDs.
- `addComments(request: AddCommentsRequest)` - Add comments within a specific CommentAnnotation.
- `getComments(request: GetCommentsRequest)` - Get comments within a specific CommentAnnotation.
- `updateComments(request: UpdateCommentsRequest)` - Update comments within a specific CommentAnnotation.
- `deleteComments(request: DeleteCommentsRequest)` - Delete comments within a specific CommentAnnotation.

#### ActivityApiService (`sdk.api.activities`)

- `addActivities(request: AddActivitiesRequest)` - Add activity log records for a document.
- `getActivities(request: GetActivitiesRequest)` - Get activity logs with optional filters (document, entity, feature/action types, user) and pagination.
- `updateActivities(request: UpdateActivitiesRequest)` - Update existing activity log records.
- `deleteActivities(request: DeleteActivitiesRequest)` - Delete activity logs by document, entity, or specific IDs.

#### AccessControlService (`sdk.api.accessControl`)

- `addPermissions(request: AddPermissionsRequest)` - Add permissions for a user on organizations, folders, or documents.
- `getPermissions(request: GetPermissionsRequest)` - Get a user's permissions for resources with access role and type details.
- `removePermissions(request: RemovePermissionsRequest)` - Remove permissions from a user for specific resources.
- `generateSignature(request: GenerateSignatureRequest)` - Generate a secure signature for Permission Provider responses.
- `generateToken(request: GenerateTokenRequest)` - Generate a JWT authentication token with user info and resource permissions.

#### CrdtService (`sdk.api.crdt`)

- `addCrdtData(request: AddCrdtDataRequest)` - Add new CRDT editor data. Supports text, map, array, and xml types.
- `getCrdtData(request: GetCrdtDataRequest)` - Get CRDT editor data. Optionally filter by editorId; omit to get all editors for a document.
- `updateCrdtData(request: UpdateCrdtDataRequest)` - Update existing CRDT editor data with new content.

#### PresenceService (`sdk.api.presence`)

- `addPresence(request: AddPresenceRequest)` - Add users to the presence list for a document.
- `updatePresence(request: UpdatePresenceRequest)` - Update presence data for existing users on a document.
- `deletePresence(request: DeletePresenceRequest)` - Remove users from the presence list for a document.

#### GdprService (`sdk.api.gdpr`)

- `deleteAllUserData(request: DeleteAllUserDataRequest)` - Delete all user data from Velt (GDPR compliance). Returns a job ID for tracking.
- `getAllUserData(request: GetAllUserDataRequest)` - Get all feature data for a user (comments, reactions, recordings, notifications) with pagination.
- `getDeleteUserDataStatus(request: GetDeleteUserDataStatusRequest)` - Get the status of a data deletion job.

#### LivestateService (`sdk.api.livestate`)

- `broadcastEvent(request: BroadcastEventRequest)` - Broadcast a live state event to a document.

#### RewriterService (`sdk.api.rewriter`)

- `askAi(request: AskAiRequest)` - Send a text rewriting or processing request to an AI model. Supports OpenAI, Anthropic, and Gemini models.

#### RecordingService (`sdk.api.recordings`)

- `getRecordings(request: GetRecordingsRequest)` - Get recording annotations for a document or organization with optional filtering and pagination.

#### WorkspaceService (`sdk.api.workspace`)

- `createWorkspace(request: CreateWorkspaceRequest)` - Create a new workspace.
- `getWorkspace(request: GetWorkspaceRequest)` - Get workspace details.
- `createApiKey(request: CreateApiKeyRequest)` - Create a new API key with optional configuration.
- `updateApiKey(request: UpdateApiKeyRequest)` - Update an API key's display name.
- `getApiKeys(request: GetApiKeysRequest)` - Get API keys with optional filtering and pagination.
- `getApiKeyMetadata(request: GetApiKeyMetadataRequest)` - Get API key metadata (plan info, owner, defaults).
- `resetAuthToken(request: ResetAuthTokenRequest)` - Reset the auth token for an API key.
- `getAuthTokens(request: GetAuthTokensRequest)` - Get auth tokens for an API key.
- `addDomains(request: AddDomainsRequest)` - Add domains to the allowed list.
- `deleteDomains(request: DeleteDomainsRequest)` - Remove domains from the allowed list.
- `getDomains(request: GetDomainsRequest)` - Get all allowed domains.
- `getEmailStatus(request: GetEmailStatusRequest)` - Check email verification status.
- `sendLoginLink(request: SendLoginLinkRequest)` - Send a login link via email.
- `getEmailConfig(request: GetEmailConfigRequest)` - Get email service configuration.
- `updateEmailConfig(request: UpdateEmailConfigRequest)` - Update email service configuration.
- `getWebhookConfig(request: GetWebhookConfigRequest)` - Get webhook service configuration.
- `updateWebhookConfig(request: UpdateWebhookConfigRequest)` - Update webhook service configuration.

#### FolderService (`sdk.api.folders`)

- `addFolder(request: AddFolderRequest)` - Add folders to an organization.
- `getFolders(request: GetFoldersRequest)` - Get folders. Optionally filter by folder ID with depth control.
- `updateFolder(request: UpdateFolderRequest)` - Update folders (rename or move).
- `deleteFolder(request: DeleteFolderRequest)` - Delete a folder and all its contents.
- `updateFolderAccess(request: UpdateFolderAccessRequest)` - Update access type for folders.

### Self-Hosting Services (`sdk.selfHosting.*`)

### CommentService

- `getComments(organizationId, commentAnnotationIds=None, documentIds=None)` - Get comments
- `saveComments(organizationId, commentAnnotation, documentId=None)` - Save comments
- `deleteComment(organizationId, commentAnnotationId)` - Delete a comment

### ReactionService

- `getReactions(organizationId, reactionAnnotationIds=None, documentIds=None)` - Get reactions
- `saveReactions(organizationId, reactionAnnotation, documentId=None)` - Save reactions
- `deleteReaction(organizationId, reactionAnnotationId)` - Delete a reaction

### AttachmentService

- `getAttachment(organizationId, attachmentId)` - Get an attachment
- `saveAttachment(organizationId, attachment, documentId=None)` - Save an attachment
- `deleteAttachment(organizationId, attachmentId)` - Delete an attachment

### UserService

- `getUsers(organizationId, userIds)` - Get users by IDs
- `saveUser(organizationId, user)` - Save a user

### TokenService

- `getToken(organizationId, userId, email=None, isAdmin=None)` - Get authentication token

## Configuration

### Database Configuration

The SDK accepts database configuration in the following format:

```python
config = {
    'database': {
        'host': 'localhost:27017',  # or 'mongodb://...' connection string
        'username': 'your-username',
        'password': 'your-password',
        'auth_database': 'admin',
        'database_name': 'velt-integration'  # Your database name,
        # Optional: override with full connection string
        # 'connection_string': 'mongodb://...'
    }
}
```

### Environment Variables

You can also use environment variables for API credentials:

- `VELT_API_KEY` - Velt API key
- `VELT_AUTH_TOKEN` - Velt auth token

## Error Handling

The SDK uses custom exceptions:

- `VeltSDKError` - Base exception
- `VeltDatabaseError` - Database-related errors
- `VeltValidationError` - Validation errors
- `VeltTokenError` - Token-related errors
- `VeltApiError` - REST API errors (network failures, unexpected errors)

```python
from velt_py import VeltSDKError, VeltDatabaseError, VeltApiError

try:
    result = sdk.selfHosting.comments.getComments(organizationId='org-123')
except VeltDatabaseError as e:
    print(f"Database error: {e}")
except VeltSDKError as e:
    print(f"SDK error: {e}")
```

## Testing

Install development dependencies:

```bash
pip install velt-py[dev]
```

Run unit tests (mocked, no credentials needed):

```bash
pytest tests/ -v --ignore=tests/integration
```

Run integration tests (hits real Velt API):

```bash
VELT_API_KEY=your-key VELT_AUTH_TOKEN=your-token \
    pytest tests/integration/ -v
```

Run all tests:

```bash
VELT_API_KEY=your-key VELT_AUTH_TOKEN=your-token \
    pytest tests/ -v
```

## License

MIT

## Support

For issues and questions, please contact support@velt.dev or visit https://docs.velt.dev

