Skip to main content

distyra

Python client for the Distyra Transaction Enrichment API: transaction enrichment and cash-flow underwriting for European lenders, built to read EU bank data correctly.

pip install distyra

What Distyra is

Transaction enrichment and cash-flow underwriting for Europe, run on EU infrastructure. It turns a raw bank descriptor into a merchant or an institutional counterparty (tax authorities, pension funds, social-security bodies, utilities), a category, and a transaction type.

  • Published pricing, no sales call. 10,000 transactions a month free, then €0.003 each pay-as-you-go, or €99 a month for 100,000 included. Bank connections are €5 per connection per month, the first one free. Full table: https://www.distyra.eu/pricing
  • Optional PSD2 bank access from the same vendor and the same API, so a connection can go straight through enrichment. The live bank and country list is at https://api.distyra.com/v1/connect/coverage.
  • Europe only. The United Kingdom is not covered.
  • EU-hosted. Compute, storage and edge are in the EU. One processing exception is declared and scoped in the DPA: https://www.distyra.eu/dpa

Base URL: https://api.distyra.com (the SDK default). The API and CDN stay on .com; only the Distyra website and customer portal use .eu.

Quick start

import os
from distyra import Distyra

client = Distyra(api_key=os.environ["DISTYRA_API_KEY"])

result = client.enrich(
    descriptor="ALBERT HEIJN 1234 AMSTERDAM NL",
    amount=-12.34,
    currency="EUR",
    mcc="5411",
)
# merchant is None for non-merchant transactions (bank fees, transfers), so guard it.
print(result.merchant.name if result.merchant else "(no merchant)")  # "Albert Heijn"
print(result.category.primary)     # "Retail & E-commerce"
print(result.category.source)      # "catalog" (curated) or "llm" (model-proposed)

Batch enrichment

batch = client.enrich_batch([
    {"descriptor": "ALBERT HEIJN 1234 AMSTERDAM NL", "amount": -12.34, "currency": "EUR"},
    {"descriptor": "NS GROEP B.V.", "amount": -4.50, "currency": "EUR"},
    {"descriptor": "NETFLIX.COM", "amount": -13.99, "currency": "EUR"},
])
for item in batch.items:
    print(item)

Up to 100 items per call; a failed item occupies its slot with an error envelope, the batch itself never fails.

Handling unknown merchants (eventual consistency)

The first time a brand-new merchant is enriched, the result may be unresolved (result.resolution.status other than resolved). The system then discovers and confirms that merchant in the background, so the same descriptor resolves shortly after, for every caller, with no model call.

Read result.resolution.retry_after:

  • A number (seconds) means a confirmed answer is likely coming. Retry the same descriptor once after that delay, or just let your next natural re-sync pick it up.
  • None means the result is definitive; no retry will help.
result = client.enrich(descriptor="TIMECHIMP UREN UTRECHT NL", country_hint="NL")
if result.resolution.status != "resolved" and result.resolution.retry_after is not None:
    # retry once after result.resolution.retry_after seconds, or wait for your next sync
    ...

resolution.retryable is a different signal: True only for a transient backend error (timed_out / transient_error), which you retry within seconds. Full guide: https://api.distyra.com/docs.

Underwriting

Statement files in, one consolidated analysis out. Multiple files for the same applicant (the "last 6 months as 6 monthly PDFs" case) consolidate into one analysis over the merged window:

analysis = client.analyze_statement(
    ["jan.pdf", "feb.pdf", "mar.pdf"],
    applicant_id="loan-2026-0142",
    input_mode="statement_pdf",     # one format per analysis
    enrich=True,                    # optional: enrich every transaction
)
print(analysis.id, analysis.features)

JSON mode, when transactions are already on hand:

analysis = client.analyze(
    applicant_id="loan-2026-0142",
    input_mode="raw",
    accounts=[{
        "account_id": "main",
        "currency": "EUR",
        "opening_balance": 1500.0,
        "transactions": [
            {"date": "2026-05-01", "amount": 2500.0, "descriptor_raw": "INVOICE 1001 ACME BV"},
            {"date": "2026-05-15", "amount": -1200.0, "descriptor_raw": "RENT MAY OFFICE"},
        ],
    }],
)

Fetch a stored analysis later with client.get_analysis(analysis_id).

Note: enrichment is self-serve; underwriting endpoints are enabled per organization. Contact sales for access.

Errors

Every non-2xx raises DistyraError with the parsed envelope:

from distyra import Distyra, DistyraError

try:
    client.enrich(descriptor="...")
except DistyraError as e:
    print(e.status_code, e.error, e.detail)

Verifying webhooks

Distyra signs every webhook delivery with a Stripe-shape header:

X-Distyra-Signature: t=<unix>,v1=<hmac_sha256_hex>

verify_webhook checks it (HMAC-SHA256 over "<t>.<raw_body>", keyed by the endpoint's signing secret, with replay protection). Always verify against the raw request body, before JSON parsing.

from distyra import verify_webhook

# Flask
@app.post("/webhooks/distyra")
def distyra_webhook():
    raw = request.get_data()  # exact bytes, before request.get_json()
    if not verify_webhook(WEBHOOK_SECRET, raw, request.headers.get("X-Distyra-Signature")):
        abort(400)
    event = request.get_json()
    # ... handle event ...
    return "", 200

verify_webhook(secret, raw_body, signature_header, *, tolerance_seconds=300) returns True only when the signature is valid and the timestamp is fresh. The default replay window is 5 minutes. Stdlib only (hmac/hashlib).

Beyond the wrapper

The full generated client (every endpoint, typed models) ships in the same distribution as distyra_api_client. The wrapper's .raw attribute is a ready-authenticated instance:

from distyra_api_client.api.catalog import get_categories

categories = get_categories.sync(client=client.raw)

Development

This package lives in the Distyra monorepo at packages/saas-sdk-python/. The distyra_api_client package is generated from the committed OpenAPI snapshot. Do not edit it by hand:

npm run snapshot -w @brightfield/saas-api   # refresh openapi.json from source
bash scripts/generate-python-sdk.sh         # regenerate distyra_api_client
bash scripts/test-python-sdk.sh             # build wheel + smoke

Release files for distyra 1.5.0

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

Source distribution (sdist)

Source distribution for distyra 1.5.0
File Size Uploaded
distyra-1.5.0.tar.gz 336.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for distyra 1.5.0
File Interpreter ABI Platform
distyra-1.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.9 MB

Release files / distyra-1.5.0.tar.gz

Download URL distyra-1.5.0.tar.gz
Size 336.5 kB
Tags Source
SHA-256 checksum
How to use checksums
85c0b778a622902e852c079d5b4438febab7621669586d1d4d252d9ef5275f60
BLAKE2b-256 checksum
How to use checksums
b0c8c26534db50b984b05648beab966c047d3905a98d95f948714b238b36eacd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / distyra-1.5.0-py3-none-any.whl

Download URL distyra-1.5.0-py3-none-any.whl
Size 1.5 MB
Tags Python 3
SHA-256 checksum
How to use checksums
cdc9d20b98e6832efee35598815a05b4d9afcb549cbc07e63b9407b3c1c259df
BLAKE2b-256 checksum
How to use checksums
8806a1d50b0f44a4da3f8c4fe9cad8fcfd67321a304d9f51fd0316c1f6f796bc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

1.6.0

2 release files

This release

1.5.0 This release

2 release files

1.4.0

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.1.0

2 release 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