Metadata-Version: 2.4
Name: velt-py
Version: 0.2.0
Summary: Python SDK for integrating Velt comments, reactions, attachments, and user management into Django applications
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,postgresql
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: python-dateutil>=2.8.2
Requires-Dist: requests>=2.25.0
Requires-Dist: boto3>=1.28.0
Provides-Extra: auth
Requires-Dist: PyJWT>=2.8.0; extra == "auth"
Requires-Dist: cryptography>=42.0.0; extra == "auth"
Provides-Extra: mongodb
Requires-Dist: pymongo[srv,tls]>=4.6.3; extra == "mongodb"
Provides-Extra: postgres
Requires-Dist: psycopg[binary,pool]>=3.1; extra == "postgres"
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"
Requires-Dist: PyJWT>=2.8.0; extra == "dev"
Requires-Dist: cryptography>=42.0.0; extra == "dev"
Requires-Dist: velt-py[mongodb]; extra == "dev"
Requires-Dist: velt-py[postgres]; extra == "dev"
Dynamic: license-file

# Velt Python SDK

Velt is an SDK to add collaborative features to your product within minutes. Example: Comments like Figma, Frame.io, Google Docs or Sheets, Recording like Loom, Huddles like Slack, and much more.

`velt-py` is the official **backend SDK** for Velt. Use it to power a self-hosted Velt backend or to call Velt's REST APIs directly from any Python service.

The SDK exposes two independent backends:

| Backend          | Namespace           | Use case                                              |
| ---------------- | ------------------- | ----------------------------------------------------- |
| **Self-hosting** | `sdk.selfHosting.*` | Store Velt data in your own MongoDB or PostgreSQL (+ AWS S3) |
| **REST API**     | `sdk.api.*`         | Call Velt's REST APIs directly — no database required |

- **Self-hosting** (`sdk.selfHosting.*`) simplifies backend implementation by up to **90%**. Pass your DB and storage configs to the SDK, call the relevant method with the raw frontend request payload, and return the response directly to the client.
- **REST API** (`sdk.api.*`) provides fully-typed `@dataclass` request objects across all Velt REST services, returning raw Velt API responses. No database or AWS configuration needed.

## Features

With Velt you can add powerful collaboration features to your backend extremely fast:

- **Comments** like Figma, Frame.io, Google Docs, Sheets and more
- **Recording** like Loom (audio, video, screen)
- **Huddle** like Slack (audio, video, screensharing)
- In-app and off-app **notifications**
- **@mentions** and assignment
- **Presence**, **Cursors**, **Live Selection**
- **Live state sync** and **multiplayer editing** with conflict resolution (CRDT)
- **Activities**, **access control**, **GDPR** data tooling, and AI-powered **agents** & **workflows**
- ... and so much more

## Installation

```bash
pip install velt-py                    # REST API backend only (no database driver)
pip install 'velt-py[mongodb]'         # + self-hosting on MongoDB
pip install 'velt-py[postgres]'        # + self-hosting on PostgreSQL
```

The core package carries no database driver. Install the extra for the database you
self-host on; a `database` config whose driver is missing fails at `initialize()` with the
exact `pip install` command to run. Without a `database` block the SDK serves the REST API
backend only, and any `sdk.selfHosting.*` use raises an error saying so.

> **Breaking change in 0.2.0:** 0.1.x installed `pymongo` unconditionally. From 0.2.0 a
> MongoDB self-hosting install must use `pip install 'velt-py[mongodb]'` (a plain
> `pip install velt-py` upgrade on a rebuilt environment will report
> `MongoDB support requires: pip install "velt-py[mongodb]"` at initialize).
> `mongoengine` and `blinker` are no longer installed either (the SDK never used them);
> declare them in your own requirements if your application does.

### Requirements

- Python 3.8+
- For self-hosting, one of:
  - MongoDB 6+ (Percona Server or MongoDB Atlas) via `velt-py[mongodb]` (`pymongo`)
  - PostgreSQL 14+ via `velt-py[postgres]` (`psycopg` 3)
- `requests` for REST API calls and `boto3` for S3 attachments (installed automatically)

## Quick Start

### Initialize the SDK

**Self-hosting** (MongoDB + optional AWS S3):

```python
from velt_py import VeltSDK

sdk = VeltSDK.initialize({
    'database': {
        'connection_string': 'mongodb+srv://user:pass@cluster.mongodb.net/velt-db',
        # The database name is taken from the URI path; add 'database_name' to override it.
        # Or pass individual components:
        # 'host': 'localhost:27017',
        # 'username': 'your-username',
        # 'password': 'your-password',
        # 'auth_database': 'admin',
        # 'database_name': 'velt-db',
    },
    'apiKey': 'YOUR_VELT_API_KEY',       # or set VELT_API_KEY
    'authToken': 'YOUR_VELT_AUTH_TOKEN', # or set VELT_AUTH_TOKEN
})
```

**REST API only** (no database needed):

```python
from velt_py import VeltSDK

sdk = VeltSDK.initialize({
    'apiKey': 'YOUR_VELT_API_KEY',
    'authToken': 'YOUR_VELT_AUTH_TOKEN',
})

# All sdk.api.* services are now available
result = sdk.api.organizations.getOrganizations(
    GetOrganizationsRequest(organizationIds=['org-123'])
)
```

### Choosing a database

The self-hosting backend stores Velt data in **MongoDB** (default) or **PostgreSQL**. The
choice is made in the `database` block; every `sdk.selfHosting.*` method behaves the same on
both, and the same test suite runs against both. Install the matching extra
(`velt-py[mongodb]` or `velt-py[postgres]`); nothing else changes between the two.

```python
sdk = VeltSDK.initialize({
    'database': {
        'type': 'postgresql',                                    # default: 'mongodb'
        'connection_string': 'postgresql://user:pass@host:5432/velt',
        # Or components: 'host': 'host:5432', 'username', 'password', 'database_name'
        # Optional: 'schema': 'public', 'sslmode': 'prefer', 'sslrootcert': None,
        #           'connect_timeout': 10, 'application_name': 'velt-py/<version>',
        #           'pool_min_size': 1, 'pool_max_size': 5, 'pool_timeout': 10,
        #           'manage_schema': True, 'connection_options': {},
    },
    'apiKey': 'YOUR_VELT_API_KEY',
    'authToken': 'YOUR_VELT_AUTH_TOKEN',
})
```

How PostgreSQL storage works:

- One table per collection (`comment_annotations`, `reaction_annotations`, `recorder_annotations`,
  `notifications`, `activities`, `attachments`, `users`) with a single JSONB `data` column
  holding the document, plus expression indexes on the fields the SDK queries.
- On first connection the SDK creates the schema, tables, and indexes (`manage_schema: True`;
  the role then needs `CREATE` on the schema). For locked-down roles set `manage_schema: False`
  and apply the DDL yourself. Generate it for *your* config (schema, `collections`,
  `user_schema`) and hand it to a migration role:

  ```python
  from velt_py.config import Config
  from velt_py.database.connection import postgres_schema_sql
  print(postgres_schema_sql(Config({'database': {'type': 'postgresql', 'connection_string': '...'}})))
  ```

  `docs/postgres-schema.sql` in the repository is that output for the defaults.
- `database_name` overrides the database named in `connection_string`, as with MongoDB.
- **TLS:** the default `sslmode: prefer` encrypts when the server offers TLS but never verifies
  the certificate and silently falls back to plaintext. For any database that is not on the
  same host use `'sslmode': 'verify-full'` with `'sslrootcert': '/path/to/ca.pem'`
  (`require` at the very least).
- Multi-process servers (gunicorn, uWSGI): each worker opens its own pool; `--preload` is
  safe (a pool inherited across `fork()` is replaced on first use in the child). First-connect
  schema management is serialized per schema with a polled advisory lock (up to 60 s), so
  concurrent starts neither race nor block each other.
- `schema` is the PostgreSQL schema that holds the tables. It is unrelated to the top-level
  `user_schema` option, which maps user fields.
- `pool_min_size` / `pool_max_size` apply to MongoDB too (previous defaults unchanged).
- Under uWSGI enable threads (`--enable-threads`): the connection pool runs maintenance threads.
- Existing MongoDB configurations are unaffected. The SDK does not migrate data between databases.

### Self-hosting example

Each self-hosting method takes a single typed resolver-request object — build it from the incoming frontend JSON with `from_dict(data)` and return the result straight to the client:

```python
from velt_py import GetCommentResolverRequest

result = sdk.selfHosting.comments.getComments(
    GetCommentResolverRequest.from_dict(data)
)
```

#### Comment `save` payload extensions

The comment `save` request mirrors the frontend contract:

- **`targetComment`** — `SaveCommentResolverRequest.targetComment` is the `PartialComment` the
  action occurred on (resolved by the frontend from `commentId`). It is request context for your
  handler only; `saveComments` does **not** persist it (the comment already lives inside the
  annotation's `comments` map).
- **`CommentResolverSaveEvent`** — when the frontend opts into additional save events, the
  `event` field carries one of these non-core values (status change, priority, assign, approve,
  reaction, subscribe, …) in addition to the core `ResolverActions`. `from_dict` parses core
  events to `ResolverActions`, additional events to `CommentResolverSaveEvent`, and any unknown
  value is preserved as a plain string.

```python
from velt_py import SaveCommentResolverRequest, ResolverActions, CommentResolverSaveEvent

request = SaveCommentResolverRequest.from_dict(data)

if request.event == CommentResolverSaveEvent.PRIORITY_CHANGE:
    ...  # react to an annotation-level priority change
elif request.targetComment is not None:
    ...  # the specific comment the action targeted

sdk.selfHosting.comments.saveComments(request)
```

#### Verifying the forwarded resolver token

When the Velt frontend forwards an auth credential to your resolver endpoint (e.g. an
`Authorization: Bearer <token>` header), `sdk.selfHosting.verifyToken(...)` authenticates it before
you serve any data. It is opt-in via a `resolver_auth` config block, framework-agnostic (pass a
headers mapping or a raw token), and **fail-closed** — it returns a structured `VerifyTokenResult`
and never raises for a verification outcome.

Install the JWT extra if you use the built-in verifier (the custom-callback path needs nothing
extra):

```bash
pip install 'velt-py[auth]'
```

```python
sdk = VeltSDK.initialize({
    'database': {...},
    'resolver_auth': {
        # Built-in JWT/JWKS verifier:
        'jwt': {
            'secret': 'your-hmac-secret',          # HS*  — or, for RS*/ES*:
            # 'public_key': '-----BEGIN PUBLIC KEY-----...',
            # 'jwks_url': 'https://your-idp/.well-known/jwks.json',
            'algorithms': ['HS256'],               # REQUIRED allowlist (rejects alg=none / confusion)
            'issuer': 'https://your-idp',          # optional, enforced when set
            'audience': 'velt',                    # optional, enforced when set
            'leeway': 30,                          # optional clock skew (seconds)
            # 'require': ['exp'],                  # optional: reject tokens missing these claims
        },
        # OR a custom escape hatch (takes priority over `jwt`):
        # 'verify': lambda token, headers: my_decode(token),  # return claims | None
    },
})

result = sdk.selfHosting.verifyToken(headers=request.headers)   # or token='...'
if not result.verified:
    return HttpResponse(status=401)        # result.errorCode tells you why
# result.claims holds the decoded payload
sdk.selfHosting.comments.saveComments(SaveCommentResolverRequest.from_dict(data))
```

`verifyToken` **authenticates only — it does not authorize**. The resolver services keep their own
`apiKey`/`organizationId` scoping, and `result.claims` is informational: if you need tenant
isolation, assert the relevant claim (e.g. an org id) against the resolver payload yourself.

### REST API example

Each `sdk.api.*` method takes a single typed request dataclass:

```python
from velt_py.models.comment_annotation_api import AddCommentAnnotationsRequest

sdk.api.commentAnnotations.addCommentAnnotations(
    AddCommentAnnotationsRequest(
        organizationId='org-123',
        documentId='doc-1',
        commentAnnotations=[{
            'location': {'id': 'section-1', 'locationName': 'Introduction'},
            'commentData': [{
                'commentText': 'This needs review',
                'from': {'userId': 'user-1', 'name': 'John Doe', 'email': 'john@example.com'},
            }],
        }],
    )
)
```

### Framework integration

Initialize the SDK once and reuse it across requests. For example, with FastAPI:

```python
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from velt_py import VeltSDK, GetCommentResolverRequest

app = FastAPI()
sdk = VeltSDK.initialize({'database': {'connection_string': 'mongodb+srv://...'}})

@app.post('/api/velt/comments/get')
async def get_comments(request: Request):
    data = await request.json()
    result = sdk.selfHosting.comments.getComments(GetCommentResolverRequest.from_dict(data))
    return JSONResponse(content=result, status_code=result.get('statusCode', 200))
```

See the [Python SDK documentation](https://velt.dev/docs/backend-sdks/python) for Django, Flask, and FastAPI integration guides.

### Shutdown

Call `sdk.close()` during graceful shutdown to release the database connection pool. The
connection is shared by every `VeltSDK` instance in the process, so `close()` on one closes it
for all:

```python
sdk.close()
```

## Development

Contributors verify changes against **real databases** and a **real framework app** before
opening a PR, using the built wheel rather than the source tree where it matters. See
[demos/README.md](demos/README.md) for the full contract and
[docs/](docs/README.md) for the design documents.

```bash
pip install -e '.[dev]'
make db-up                                   # MongoDB + PostgreSQL via Docker
make test-unit                               # mocked unit tests
make test-selfhost DB=mongodb                # SDK against the real database
make test-demo-django DB=mongodb SDK=wheel   # Django demo running the built wheel
make check                                   # everything CI runs, minus secrets
```

CI runs the same layers on every push and PR without secrets (`Self-hosting Live`, `Demo
Django`); `Verification complete` is the single required check. The publish workflow reuses
the wheel that passed the demo.

## Documentation

- Read the [Python SDK documentation](https://velt.dev/docs/backend-sdks/python) for the full setup guide, configuration reference, and a complete list of `sdk.selfHosting.*` and `sdk.api.*` methods with request/response examples.
- Browse the broader [Velt documentation](https://docs.velt.dev/get-started/overview) for guides and frontend SDK references.
- [velt-py on PyPI](https://pypi.org/project/velt-py)

## Use cases

- Explore [use cases](https://velt.dev/use-case) to learn how collaboration could look on your product.
- [Figma Template](https://www.figma.com/community/file/1402312407969730816/velt-collaboration-kit): visualize what collaboration features could look like on your product.

## Releases

- See the [latest changes](https://docs.velt.dev/release-notes/).

## Security

- Velt is SOC2 Type 2 and HIPAA compliant. [Learn more](https://velt.dev/security)

## Community

- [X](https://x.com/veltjs): updates, announcements, and general Velt tips.
- [Discord](https://discord.gg/GupvcYH27h): ask questions and share tips.

## License

MIT

## Support

For issues and questions, contact support@velt.dev or visit [docs.velt.dev](https://docs.velt.dev).
