Skip to main content

Epistola Python Client

Python client library for the Epistola document generation API, generated from the Epistola OpenAPI contract with OpenAPI Generator (python / urllib3, pydantic v2 models).

It adds, on top of the stock generated client:

  • Client identity headers (User-Agent + X-EP-Node-Id) required on every request.
  • RFC 9457 problem-detail error handling — typed ProblemDetailException with a type_slug discriminator and generated KnownProblemSlugs constants.
  • Self-signed JWT authentication (JwtSigner), minting a fresh short-lived token per request.
  • Static API-key authentication via Authorization: ApiKey <key>.
  • NDJSON result collection (ResultCollector) with adaptive polling, compression, and partition-aware routing helpers.
  • Client-side JSON Schema validation of template data (TemplateSchemaValidator).

The package version tracks the Epistola contract version (info.version) and releases in lockstep with the Kotlin and .NET clients.

Install

pip install epistola-client

Prerelease snapshots (published on every push to main) are available from TestPyPI:

pip install -i https://test.pypi.org/simple/ \
  --extra-index-url https://pypi.org/simple/ epistola-client

Quick start

from epistola_client import (
    EpistolaClientBuilder, ClientIdentity, JwtSigner, TemplatesApi,
)

identity = ClientIdentity.builder().node_id("my-pod-123").build()
signer = (
    JwtSigner.builder()
    .consumer_id("invoice-service")
    .private_key(JwtSigner.load_private_key("private.pem"))
    .build()
)

http = (
    EpistolaClientBuilder()
    .base_url("https://epistola.example.com/api")
    .identity(identity)
    .jwt_signer(signer)
    .install_problem_detail_handler()
    .build()
)

templates = TemplatesApi(http)
template = templates.get_template("acme", "invoices", "invoice")

For static tenant API keys, use .api_key("epk_...") instead of .jwt_signer(...). The legacy X-API-Key header remains supported for existing integrations, but is deprecated. Some Epistola Suite deployments may disable API-key authentication entirely; with .install_problem_detail_handler(), switch on e.type_slug == KnownProblemSlugs.API_KEY_AUTH_DISABLED and guide the caller to JWT auth.

Error handling

Install the opt-in problem-detail handler on the client (.install_problem_detail_handler()), then switch on type_slug against KnownProblemSlugs. The slug list is open — the API can introduce new problem types without a client release — so always keep a fallback branch and fall back to the HTTP status for unrecognized types.

from epistola_client import (
    TenantsApi, ProblemDetailException, KnownProblemSlugs,
)

try:
    tenants_api.get_tenant("acme")
except ProblemDetailException as e:
    match e.type_slug:
        case KnownProblemSlugs.NOT_FOUND:
            log.warning("tenant not found: %s", e.detail)
        case KnownProblemSlugs.FORBIDDEN:
            raise PermissionError(e.detail)
        case KnownProblemSlugs.VALIDATION_ERROR:
            for err in e.errors:               # field-level ValidationProblemDetail
                log.warning("%s: %s", err.var_field, err.message)
        case KnownProblemSlugs.DATA_MODEL_VALIDATION_ERROR:
            for example, failures in e.validation_errors.items():  # 422 data-model failures
                for f in failures:
                    log.warning("%s %s: %s", example, f.path, f.message)
        case _:
            raise                              # unknown / framework error — fall back to status

ProblemDetailException extends the generated ApiException, so existing except ApiException sites keep working. It exposes type, type_slug, title, problem_status, detail, errors, validation_errors, is_validation_problem, and is_data_model_validation_problem.

Result collection

from epistola_client import ResultCollector

collector = (
    ResultCollector.builder()
    .api_client(http)
    .tenant_id("acme")
    .handler(lambda result: print(result.request_id, result.status))
    .build()
)
collector.start()   # blocks; adaptive polling until collector.stop()

Development

The client is generated from the bundled spec. From the repository root:

make bundle                       # produce openapi.yaml
cd client-python-urllib3
./generate.sh                     # stock client + derived sources
uv run pytest                     # run the tests

generated/ (stock client) and src/epistola_client/_generated/ (derived sources) are gitignored and rebuilt from the spec each time.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

epistola_client-0.14.0.tar.gz (172.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

epistola_client-0.14.0-py3-none-any.whl (571.5 kB view details)

Uploaded Python 3

File details

Details for the file epistola_client-0.14.0.tar.gz.

File metadata

  • Download URL: epistola_client-0.14.0.tar.gz
  • Upload date:
  • Size: 172.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for epistola_client-0.14.0.tar.gz
Algorithm Hash digest
SHA256 47cfe96828bf220b2a9b88129d8f7885bd838ab0f6970bf0862822b4b728e467
MD5 6b9f78d102310dea6552663499dabb00
BLAKE2b-256 6c49ed54cad83072901cd62ec4de2b15e35afdb7136d4e5a782824e146855d8a

See more details on using hashes here.

Provenance

The following attestation bundles were made for epistola_client-0.14.0.tar.gz:

Publisher: release.yml on epistola-app/epistola-contract

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file epistola_client-0.14.0-py3-none-any.whl.

File metadata

File hashes

Hashes for epistola_client-0.14.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7d0413503c9c53dd75f93e64f4a3737a243c243ddf159142b214a591dc1d9101
MD5 bec94dbd0302b52989e4ffdd6acfb9bd
BLAKE2b-256 4dc84e42c0a881fc4915f3e7f7063bac9596a9304126145be6784482ab59524a

See more details on using hashes here.

Provenance

The following attestation bundles were made for epistola_client-0.14.0-py3-none-any.whl:

Publisher: release.yml on epistola-app/epistola-contract

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

1.2.0

2 files

1.1.0

2 files

1.0.1

2 files

1.0.0

2 files

0.16.1

2 files

0.16.0

2 files

0.15.0

2 files

This release

0.14.0 This release

2 files

0.13.0

2 files

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