Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

ovok-sdk (Python)

Python SDK for the Ovok API. It follows the @ovok/core/next TypeScript contract: the same namespaces and routes, with method names in snake_case (searchOne is search_one). It talks to the Ovok API directly and uses only the Python standard library.

Install

pip install ovok-sdk

Or with uv:

uv add ovok-sdk

pip install ovok-sdk installs final releases only. Release candidates (0.1.0rc<n>) and development builds (0.1.0.dev<N>) are pre-releases on PyPI:

pip install --pre ovok-sdk
pip install "ovok-sdk==0.1.0.dev<N>"

Python 3.10 or later.

Create a client

from ovok_sdk import Ovok

ovok = Ovok("https://api.sandbox.ovok.com", "your-tenant")
Option Default Notes
base_url Required. An absolute http(s) URL.
tenant_code Required. Picks the tenant at sign-in and names the storage key.
fhir_path "fhir" The FHIR base under base_url, such as "fhir/R4".
storage MemoryStorage() Anything with get_item, set_item and remove_item.
client_id Your client application's ID.
client_secret Makes this a machine client. Requires client_id.
transport UrllibTransport() A callable from OvokRequest to OvokResponse, for proxies or tests.
timeout 30.0 Seconds per request.
max_network_retries 2 For GET, HEAD, PUT, DELETE and conditional creates only.
on_unauthenticated Called with an UnauthenticatedReason when a refresh token is refused.

A bad option raises OvokConfigError from the constructor, not from the first request.

Sign users in

result = ovok.auth.login(profile_type="Practitioner", email="ada@example.com", password=password)

if result.status == "mfa-required":
    result = ovok.auth.verify_mfa(result.challenge, code_from_authenticator_app)

session = ovok.auth.current()
ovok.auth.logout()
  • Login flow: login runs the tenant login with PKCE and signs in to the configured tenant_code. profile_type is "Patient", "Practitioner" or "RelatedPerson".
  • Session storage: the session is saved under ovok:<tenant_code>:session, as JSON in the same shape the TypeScript SDK writes, so the SDKs can share one store.
  • Refresh: access tokens refresh a minute before they expire, and concurrent callers share one refresh.
    • A refused refresh token (400, 401 or 403) signs the user out and calls on_unauthenticated.
    • Any other failure keeps the session and raises, so a later call can retry.
  • Logout: logout always clears the local session, even when the server cannot be reached.
  • Also on ovok.auth: register, register_practitioner, social_login, me, change_password, reset_password, complete_password_reset, delete_account, and sessions.list / sessions.revoke.
from ovok_sdk import PatientRegistration

ovok.auth.register(PatientRegistration("ada@example.com", password, password, "Ada", "Lovelace"))

Machine clients

On a server, pass client_id and client_secret from a client application. The client fetches its own token from /oauth2/token and renews it before it expires. A machine client cannot sign users in: those methods raise OvokConfigError before sending anything.

import os

ovok = Ovok(
    os.environ["OVOK_BASE_URL"],
    os.environ["OVOK_TENANT_CODE"],
    client_id=os.environ["OVOK_CLIENT_ID"],
    client_secret=os.environ["OVOK_CLIENT_SECRET"],
)

FHIR

patient = ovok.fhir.read("Patient", patient_id)

page = ovok.fhir.search("Observation", {"patient": f"Patient/{patient_id}", "_count": 50})
for observation in page.auto_paging_iter():
    ...

ovok.fhir.create(observation, if_none_exist="identifier=urn:device|42")
ovok.fhir.update(patient)
ovok.fhir.patch("Patient", patient_id, [{"op": "replace", "path": "/active", "value": False}])
ovok.fhir.delete("Patient", patient_id)
ovok.fhir.batch(bundle)
first = ovok.fhir.search_one("Patient", {"identifier": "urn:mrn|123"})
  • Search parameters: a list value repeats its parameter, which FHIR reads as AND, as in {"date": ["ge2026-01-01", "le2026-02-01"]}. For OR, write one comma-separated string.
  • Resources are plain dicts in FHIR's own JSON shape.

Patient and measurements

ovok.patient provides FHIR-backed appointments, schedules, slots, measurements, care_plans, questionnaires, questionnaire_responses, devices and notifications. It also supports latest_observations() and dashboard(). List methods return resource lists; dashboard() reads the signed-in profile and its latest observations, measurement history and care plans.

ovok.measurements builds and saves observations for the measurement types in MeasurementTypeKey. Measurement fields use snake_case, while generated FHIR resources retain FHIR's camelCase JSON fields:

from ovok_sdk import MeasurementTypeKey

result = ovok.measurements.save({
    "patient_id": "patient-id",
    "measurement": {
        "measurement_type_key": MeasurementTypeKey.heart_rate,
        "heart_rate": 72,
    },
})

The namespace also supports build, save_observations, latest, last_for_patient, codes_for and offline_queue.flush. Saves use conditional FHIR entries, link related observations, split large bundles, and queue retryable failures in the configured storage. Pass identifier_systems and replace=True to update an observation already saved under one of those reading identifiers.

Questionnaires and content

ovok.questionnaires.populate(questionnaire_id) populates a questionnaire. ovok.questionnaire_responses supports submit, submit_with_observations and extract. ovok.content provides search, retrieve, create, update, delete, map, copy_from_parent and copy_all_from_parent for language-scoped CMS content.

Documents and integrations

ovok.documents supports list, upload creation and replacement, metadata updates, delete, download_url and public_download_url. Download URLs are constructed locally; pass query values such as width, height and fit when needed.

ovok.video_calls groups appointments, access and emails, and provides notify_active. ovok.ai provides translate, fhir_search and chat sessions/messages. ovok.wearables groups provider and connection operations. ovok.bots.execute(identifier, input) runs a bot.

Platform namespaces

  • ovok.projects.list() lists projects the signed-in user administers. Its members namespace supports list, invite, update and remove; features supports retrieve and update; settings supports retrieve, update and update_text.
  • ovok.accounts.list() reads the signed-in user's accounts.
  • ovok.locales.retrieve() and update() manage project locales. ovok.localizations supports list, retrieve, update and delete; localizations.i18next reads and writes the translation document.
  • ovok.email.templates.list() reads the available email templates.
  • ovok.invites exposes record_shares, care, practitioners and patients flows, each with create and accept. invites.accept(body) selects the record-share or care flow from the signed-in profile.
  • ovok.sharing exposes invites.create / redeem, shares.list / revoke, and settings.retrieve / update.

Slim and Signals

ovok.slim groups the Slim API resources. patients supports list, retrieve, create, update, delete, everything and current_vitals, with patient thresholds.retrieve / update. devices supports CRUD, assign_to_project, live, live_telemetry, telemetry, and effective or last observation ID reads. Concurrent identical live reads share one request. notifications supports list and recent; its notes resource supports list, create, update, patch and delete.

Other Slim resources are thresholds (retrieve, update), trends (graphs, realtime), sleep.statistics, locations (CRUD and list), zones (list, retrieve, device_counts), exports.patient_pdf, change_history.list, projects (retrieve, update, users, current_users, device_counts, patient_counts, and children.list / create / delete), policies (CRUD and list), organizations (CRUD), practitioners.invite, users.delete and health().

devices = ovok.slim.devices.live({"page": 0, "count": 20})
resident = ovok.slim.patients.retrieve("patient-id")
unreachable = ovok.signals.undeliverable.list()

ovok.signals also supports enable(patient_id) and gateway.request(method, path, query=..., body=...). Gateway paths are lists of safe path segments. undeliverable.repair() repairs the residents returned by list(); repair({"patientIds": [...]}) limits the repair.

CMS

ovok.cms.legal_page(slug), translations(locale), documents.list(collection, params) / retrieve(collection, slug, params), and release_notes.list(params) read published content. CMS requests include the client's tenant_code; collection queries support locale, page, limit and sort.

  • ovok.health.check() reads the healthcheck. It always answers 200, so read ok.
  • ovok.consent: legal.retrieve(), legal.set(accepted), medical.retrieve(), medical.set(accepted) and request_signature(consent_id).

Paging

A list call returns a Page with data, has_more and total (None when the server does not say). auto_paging_iter() walks every item from that page on, fetching later pages as it goes, and next_page() fetches one page.

Errors

Every failure is an OvokError with status, code, type, message, trace_id, request_id, retry_after_ms and field_errors. Catch a subclass to handle one case:

Class When
OvokValidationError 400, 422, or a request the SDK refuses to send
OvokAuthenticationError 401
OvokPermissionError 403: signed in, but not allowed
OvokNotFoundError 404
OvokConflictError 409
OvokRateLimitError 429
OvokServiceError 5xx
OvokConnectionError No answer: network failure or timeout
OvokConfigError A bad option, or an action this client cannot take

Quote trace_id when you report a problem. The x-trace-id, x-request-id and Retry-After headers win over the same details in the body.

Documentation

See the Python SDK documentation for guides and the API reference, generated from the docstrings.

Development

uv run python -m unittest discover -s tests
uv run ruff check
uv run --group docs python scripts/generate_reference.py

ruff check fails on a missing or malformed docstring. generate_reference.py writes the API reference pages to docs/reference/; see AGENTS.md for the docs rule.

Tests never reach a server: tests/fake_transport.py queues replies, and tests/route_harness.py turns a namespace call into a line such as "GET v1/me/consent/legal" (request_line) or its JSON body (request_body).

Releases

The release workflow publishes to PyPI on every push:

Branch Git tag PyPI version
development v0.1.0-dev.<N>+<sha7> 0.1.0.dev<N>
staging v0.1.0-rc.<n> 0.1.0rc<n>
main v0.1.0 0.1.0

N is the commit count of development. The base version is version in pyproject.toml. Bump it after a final release; the workflow refuses to publish dev or rc builds of a version that is already released.

License

GNU Affero General Public License v3.0.

Metadata

Release files for ovok-sdk 0.1.0rc4

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

Source distribution (sdist)

Source distribution for ovok-sdk 0.1.0rc4
File Size Uploaded Provenance Actions
ovok_sdk-0.1.0rc4.tar.gz 76.3 kB Without provenance Details for ovok_sdk-0.1.0rc4.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for ovok-sdk 0.1.0rc4
File Interpreter ABI Platform Actions
ovok_sdk-0.1.0rc4-py3-none-any.whl Python 3 none any Details for ovok_sdk-0.1.0rc4-py3-none-any.whl

Total release size: 167.4 kB

Release files / ovok_sdk-0.1.0rc4.tar.gz

Download URL ovok_sdk-0.1.0rc4.tar.gz
Size 76.3 kB
Tags Source
SHA-256 checksum
How to use checksums
c0acb162191305424d8a625547e688e2518f737a9fbeb1ade1b66a0457cc7102
BLAKE2b-256 checksum
How to use checksums
388351e2380dc138fd42d71baebb89d6237920b04100b6525a96e441403e5749
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / ovok_sdk-0.1.0rc4-py3-none-any.whl

Download URL ovok_sdk-0.1.0rc4-py3-none-any.whl
Size 91.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
70c5be4ada3beeeb5270aa558172d2a73085564176d179f4b3153ee1fc023888
BLAKE2b-256 checksum
How to use checksums
4809e02bff19ee50e99807b4dabb60d7b86d13afc4299df83313ac41cff5083a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for ovok-sdk 0.1.0rc4

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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