Metadata-Version: 2.4
Name: notex-python
Version: 0.1.0
Summary: Typed, dependency-free Python SDK for the NoteX API
Author: NoteX
License-Expression: MIT
Project-URL: Homepage, https://notexapp.com
Project-URL: Documentation, https://be-docs.notexapp.com
Keywords: notex,ai,notes,sdk,api-client
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
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: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: test
Requires-Dist: pytest>=7; extra == "test"
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == "dev"
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: pyright>=1.1.400; extra == "dev"
Requires-Dist: ruff>=0.12; extra == "dev"
Requires-Dist: twine>=6; extra == "dev"
Dynamic: license-file

# NoteX Python SDK

Typed, dependency-free Python SDK for API-key-enabled NoteX endpoints.

```bash
pip install notex-python
```

```python
from notex import NotexClient

client = NotexClient(api_key="ntx_live_xxx")
```

Requires Python 3.9 or newer.

## Naming convention

The public SDK has one flat, explicit naming convention:

| Operation | Convention | Examples |
| --- | --- | --- |
| Read one value | `get_<resource>()` | `get_profile()`, `get_task_result()` |
| Read a collection | `list_<resources>()` | `list_notes()` |
| Validate input | `validate_<resource>()` | `validate_source()` |
| Create content | `create_<resource>()` | `create_note()`, `create_quiz()` |
| Wait for an async operation | `wait_for_<resource>()` | `wait_for_task()` |

Sync and async clients expose the same method names. The async variants are
awaited.

## Multi-user integration

Each client represents one user API key for one request or background job.
Do not mutate a singleton client's key between users.

```python
from notex import NotexClient


def notex_for_connection(connection_id: str) -> NotexClient:
    api_key = credential_store.decrypt(connection_id)
    return NotexClient(api_key=api_key)
```

The SDK does not create, list, delete, persist, or automatically load API
keys. The integrating backend owns credential storage and rotation.

## Supported methods

### Account and notes

```python
credits = client.get_credits()
quota = client.get_quota()
profile = client.get_profile()
notes = client.list_notes(limit=20, sort_field="createdAt", sort_order=-1)
```

Credit fields vary by account/plan. `Credits` exposes optional
`total_credits`, `reward_credits`, `purchased_credits`, `user_type`, and
`has_paid`, while `raw` preserves the complete backend payload. `balance` is
an optional compatibility alias for responses that provide `balance` or
`total_credits`.

### Validate and create notes

Create a note from a web URL:

```python
submission = client.create_note(
    web_url="https://youtu.be/example",
    language_hints=["en"],
)
```

Convenience equivalent:

```python
submission = client.create_note_from_url(
    "https://youtu.be/example",
    language_hints=["en"],
)
```

Create a note from an existing NoteX file URL:

```python
submission = client.create_note_from_file_url(
    "audio/user-1/lecture.mp3",
    language_hints=["en"],
)
```

Create a note from a local file:

```python
submission = client.create_note_from_file(
    "./lecture.mp3",
    upload_file_name="lecture.mp3",
    language_hints=["en"],
    content_type="audio/mpeg",
)
```

The local-file method performs the complete internal flow:

1. Request a presigned upload contract.
2. Upload the file directly without the NoteX API key.
3. Submit note creation using the returned `file_url`.

Presign and direct-upload helpers are intentionally private. Use
`upload_file_name` when the local filename contains characters rejected by the
target gateway or object storage.

Validate a source before spending credits:

```python
validation = client.validate_source(web_url="https://youtu.be/example")
```

Exactly one of `web_url` or `file_url` is accepted by `create_note()` and
`validate_source()`.

### Create content from a note

All create methods return `TaskSubmission` with a `task_id`.

```python
flashcards = client.create_flashcards(
    "note-id",
    num_cards=10,
    difficulty="medium",
)
quiz = client.create_quiz("note-id", num_questions=10)
mindmap = client.create_mindmap("note-id")
podcast = client.create_podcast("note-id", duration=180)
shorts = client.create_shorts("note-id", voice_id="en-US-Standard-A")
quiz_video = client.create_quiz_video("note-id", voice_id="en-US-Standard-A")
slides = client.create_slide("note-id", template_id="default", language="en")
translation = client.create_translation("note-id", language="vi")
```

### Poll task results

Use `get_task_result()` when the integrating backend manages scheduling:

```python
result = client.get_task_result(submission.task_id)
```

Use `wait_for_task()` in a worker when a blocking helper is appropriate:

```python
result = client.wait_for_task(
    submission.task_id,
    poll_interval=5,
    timeout=120,
)
```

Do not run file uploads or blocking task polling directly in a web request.
Use a durable queue or background worker.

Completed results can be parsed into feature-specific typed models:

```python
from notex import FlashcardSet

result = client.wait_for_task(flashcards.task_id)
flashcard_set = FlashcardSet.from_task_result(result)
print(flashcard_set.cards[0].front)
```

Available result models include `GeneratedNote`, `FlashcardSet`, `QuizSet`,
`Mindmap`, `Podcast`, `Video`, and `SlideDeck`.

## Async client

`AsyncNotexClient` exposes the same names and runs blocking standard-library
I/O in worker threads so the event loop remains responsive.

```python
from notex import AsyncNotexClient

client = AsyncNotexClient(api_key="ntx_live_xxx")

submission = await client.create_quiz("note-id", num_questions=10)
result = await client.wait_for_task(submission.task_id, timeout=120)
```

## Errors and retry behavior

```python
from notex import NotexAuthenticationError, NotexRateLimitError

try:
    client.get_credits()
except NotexAuthenticationError:
    reconnect_notex_account()
except NotexRateLimitError as error:
    reschedule_job(error.retry_after)
```

Public errors:

- `NotexAPIError`
- `NotexAuthenticationError`
- `NotexPermissionError`
- `NotexRateLimitError`
- `NotexUploadError`
- `NotexTaskError`

GET and HEAD requests retry transient network failures and HTTP 429/5xx
responses, honoring `Retry-After`. Content-creating POST requests are not
retried by default because repeating them can create duplicate work.

## API compatibility

Every high-level method accepts `endpoint=` and `base_url=` overrides. This
allows an integration to adopt a new backend version before the SDK releases
an update.

```python
client.get_credits(endpoint="/v3/credits/me")
client.create_flashcards("note-id", endpoint="/v7/create/flashcards")
```

For a documented endpoint that is not wrapped yet, use the low-level escape
hatch:

```python
response = client.request(
    "POST",
    "/v10/feature",
    form={"note_id": "note-id"},
    headers={"Idempotency-Key": "internal-job-id"},
)
```

## Real API integration tests

Copy the safe template and put the real key/base URL in the local file:

```powershell
Copy-Item .notex-test.env.example .notex-test.env
```

Edit `.notex-test.env`:

```dotenv
NOTEX_TEST_API_KEY=ntx_test_your_real_key
NOTEX_TEST_BASE_URL=https://api.notexapp.com
```

The file is ignored by Git. Read-only tests cover profile, credits, quota, and
note listing:

```bash
python -m pytest tests/test_staging_integration.py -v
```

Creation tests can upload files and spend credits, so they require explicit
opt-in:

```dotenv
NOTEX_RUN_WRITE_TESTS=1
NOTEX_TEST_WEB_URL=https://youtu.be/example
NOTEX_TEST_FILE=C:\path\to\lecture.mp3
NOTEX_TEST_NOTE_ID=existing-note-id
NOTEX_TEST_FEATURES=flashcards,quiz,mindmap,translation
```

Optional feature configuration is documented in
`.notex-test.env.example`. Test output is not persisted by the suite; local
credentials and `.notex-test-results/` are both excluded from Git.

## Development and release checks

```bash
python -m pip install -e ".[dev]"
python -m ruff check .
python -m pyright
python -m pytest -q
python -m build
python -m twine check dist/*
```

See [RELEASING.md](RELEASING.md) for the Trusted Publishing release flow.
Never log, commit, or return API keys to frontend clients.
