python-openarch-api
Python client for the REST API of Hyland OpenArch, an open source project that federates unstructured, AI-ready data from all sources across an organization and makes it available to AI services and agents while always respecting permissions. OpenArch can be self-managed and used with your own LLMs and choice of AI services. Sync and async, with Pydantic v2 models.
- Every operation of
docs/openapi/openarch-api.yaml: documents by id and path (with conditional writes and upsert), query / advanced / named / terms aggregation / vector search, download, users and groups, repository model and security policies (v1 and v2), vocabularies, upload. - Pydantic v2 models generated from the spec, plus hand-written
OpenArchDocumentandOpenArchQueryResult: documents carry dynamiccin_*properties the generated types do not type (the same choice content-lake-app makes in Java). OpenArchClient(sync) andAsyncOpenArchClient(async), on httpx. The sync client is generated from the async one, so the two never drift.- Basic auth (a local OpenArch), bearer token (the cloud CIC) — a string or a function returning
the current one — or any
httpx.Auth.
Status: alpha (0.1.0). Written against the spec generated by running OpenArch
(docs/openapi/openarch-api.yaml), first from main aa90475b (2026-10-13, 12.0-SNAPSHOT). The
spec was regenerated from main 34caff31 (2026-10-19), the version that added the BasicAuth
security scheme, its only change: until then the spec declared only the bearer token of the cloud
CIC (Hyland Content Innovation Cloud). Checked again on 2026-10-21 against 28f4b833 (unchanged);
see docs/openapi/README.md. Unit-tested with mocks and tested against a
local OpenArch in Docker (tests/test_live.py).
Install
uv pip install python-openarch-api
For development with the source, install from a clone instead: uv pip install -e ".[dev]" (see
Development).
Use
Every method, model and error is described in the API reference, docs/api.md.
from python_openarch_api import OpenArchClient, OpenArchDocument
with OpenArchClient("http://localhost:8080", username="admin", password="password") as oa:
print(oa.hello(protected=True))
# Query (HXQL)
result = oa.query("SELECT * FROM SysContent WHERE cin_sourceId = 'repo-1'", limit=50)
for doc in result.documents:
print(doc.sys_id, doc.sys_name, doc.cin_id, doc.properties)
# Next page: pass the cursor back
more = oa.query("SELECT * FROM SysContent WHERE cin_sourceId = 'repo-1'", limit=50,
search_after=result.next_search_after)
# Create a document carrying source-system fields (needs the CinRemote mixin)
doc = OpenArchDocument(sys_primary_type="SysFile", sys_name="report.pdf",
cin_id="node-1", cin_source_id="repo-1").with_cin_remote()
created = oa.create_document_by_path("/alfresco-sync", doc)
# Partial update, only if the title is still the old one
oa.patch_document(created.sys_id, [{"op": "replace", "path": "/sys_title", "value": "Report"}],
if_='content.sys_title == "report.pdf"')
Async:
from python_openarch_api import AsyncOpenArchClient
async with AsyncOpenArchClient("https://openarch.example.com", token=get_access_token) as oa:
doc = await oa.get_document("a1b2c3")
Documents and dynamic properties
OpenArchDocument has the schema's sys_* fields (snake_case in Python, e.g.
sys_primary_type; their wire names on the way out) and the CinRemote fields cin_id,
cin_source_id, cin_paths, cin_ingest_properties, cin_ingest_property_names, cin_read,
cin_deny. Any other property (repository-model fields, sysembed_*) is kept as is:
doc.properties # {"my_customField": 42, ...} (wire names)
doc.get("cin_sourceId") # any property by wire name
doc.to_json() # request body: wire names, None left out
OpenArch rejects a document with cin_* fields unless sys_mixinTypes includes CinRemote;
with_cin_remote() adds it.
Binary content
A blob property points at its content by uploadId. Send the content in the same request (the
part name is yours to pick), or upload it first:
oa.create_document_by_path("/docs", {"sys_primaryType": "SysFile", "sys_name": "a.txt",
"sysfile_blob": {"uploadId": "f1"}},
files={"f1": ("a.txt", data, "text/plain")})
up = oa.upload(data, file_name="a.txt", mime_type="text/plain")
oa.create_document_by_path("/docs", {"sys_primaryType": "SysFile", "sys_name": "a.txt",
"sysfile_blob": {"uploadId": up.id}})
oa.download(doc_id, "sysfile_blob/a.txt") # -> bytes
Writes
update_document, patch_document, move (and the _by_path ones) take condition= (an HXQL
predicate) or if_= (an expression), user_change=False for technical updates that should not
bump sys_ugen, and on PUT upsert=True to create the document when it does not exist.
Model and security policies: v1 and v2
get_model / set_model / get_security_policies / set_security_policies are the v1 API. The
_v2 methods return and take a version for optimistic locking: read with get_*_v2, change,
and send the same object back with set_*_v2.
Repository
The HXCS-REPOSITORY header is sent on every request (repository="default"); pass
repository= to the client or to a call to use another one.
Errors
Non-2xx answers raise OpenArchError (status_code, error = the parsed
RepositoryErrorResponse), or its subclasses OpenArchAuthError (401/403),
OpenArchNotFoundError (404), OpenArchConflictError (409: a set_*_v2 with a stale version)
and OpenArchPreconditionError (412: a condition / if_ that did not match, or checkin of a
non-versionable document).
Development
Create and activate a virtual environment, then install the package with the dev extra.
Linux / macOS:
uv venv venv-3.14 --python 3.14
source venv-3.14/bin/activate
uv pip install -e ".[dev]"
python -m pytest
Windows (PowerShell or Command Prompt):
uv venv venv-3.14 --python 3.14
venv-3.14\Scripts\activate
uv pip install -e ".[dev]"
python -m pytest
With the virtual environment active, the same on every platform:
python scripts/generate_models.py # after updating docs/openapi/openarch-api.yaml
python scripts/unasync.py # after editing python_openarch_api/_async/client.py
The dev extra (-e ".[dev]") is needed to run the tests and the scripts: it brings pytest,
PyYAML (a test reads the spec) and the pinned datamodel-code-generator. A plain
uv pip install -e . installs only httpx and Pydantic.
docs/openapi/openarch-api.yaml— the spec, downloaded from a running OpenArch (it ships no static spec); docs/openapi/README.md says how to regenerate it.python_openarch_api/models/generated.py— generated by datamodel-code-generator (pinned); do not edit.python_openarch_api/_async/client.py— the client; edit this one.python_openarch_api/_sync/client.py— generated from it; a test fails if it is stale.- A test checks that every
operationIdin the spec has a client method. tests/test_live.pyruns against a real OpenArch whenOPENARCH_URLis set (it creates and deletes a folder at the root). OpenArch's rootdocker-compose.ymlleaves the data dir unwritable, so every upload is a 500: startopenarch-appwithNUXEO_DATA_DIR=/tmp, as OpenArch's own README and CI do.
Tests
pytest collects 23 tests in total: 16 unit tests (tests/test_client.py, with mocks, no server)
and 7 live tests (tests/test_live.py), which are skipped unless OPENARCH_URL is set. To run the
live tests against a running OpenArch, set OPENARCH_URL in the shell where the virtual
environment is active. Use http://localhost:8080 for OpenArch's own default port, or
http://localhost:8088 for the OpenArch of KG Spaces Deployment (its OPENARCH_PORT, 8088 by
default).
Linux / macOS:
OPENARCH_URL=http://localhost:8080 python -m pytest tests/test_live.py
Windows (PowerShell):
$env:OPENARCH_URL = "http://localhost:8080"
python -m pytest tests/test_live.py
Windows (Command Prompt):
set OPENARCH_URL=http://localhost:8080
python -m pytest tests/test_live.py
Without tests/test_live.py, python -m pytest runs the unit tests and the live tests together.
OPENARCH_USER / OPENARCH_PASSWORD set the Basic auth user; they default to the admin user of
the local openarch-app's openarch-users.yml. Set them the same way as OPENARCH_URL.
Where the spec and the server differ
The client follows the server where the generated spec is short of it:
- Multipart document writes: the spec shows a placeholder part (
main1234); any name works, as long as a blob property references it byuploadId. nextSearchAfter: the query schemas refer to it butQueryResultdoes not declare it;OpenArchQueryResult.next_search_afterreads it.StringTermsBuckethas no properties in the spec;TermsBuckettypeskeyanddoc_count.- Responses the spec leaves untyped (named query list, v1 security policies) are returned as parsed JSON.
download_urlneeds a blob provider with pre-signed URLs (S3); the local filesystem store answers 501.
License
Apache-2.0, as is OpenArch, from which docs/openapi/openarch-api.yaml is generated.
Metadata
Release files for python-openarch-api 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| python_openarch_api-0.1.0.tar.gz | 59.4 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| python_openarch_api-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 93.4 kB
Release files / python_openarch_api-0.1.0.tar.gz
| Download URL | python_openarch_api-0.1.0.tar.gz |
|---|---|
| Size | 59.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9067e27b993b856269d30c19582bcaac7c071bf8f0f032c80a9c7b79f61821f7
|
|
BLAKE2b-256 checksum How to use checksums |
ce7ebc78d7aaedf1cc1972f581bca84b456f5c4a3327b668e99e670fbf839064
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.6 {"installer":{"name":"uv","version":"0.12.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / python_openarch_api-0.1.0-py3-none-any.whl
| Download URL | python_openarch_api-0.1.0-py3-none-any.whl |
|---|---|
| Size | 34.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
88faa48cc8ad2dbf9346d1920602d071d903501681fc3281c28dc5e7653cf88c
|
|
BLAKE2b-256 checksum How to use checksums |
252a3edbd4afeacbd1c6e4ab7a508f4e879fe05b70f9fa9553128bc9ea2b7d3e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.6 {"installer":{"name":"uv","version":"0.12.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for python-openarch-api 0.1.0
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.