Skip to main content
Pre-release

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

xyp

Typed Python SDK for XYP (ХУР), Mongolia's government data exchange system. All 499 services, sync and async, fully type-hinted.

pip install "xyp>=0.1.0a2"     # or: uv add "xyp>=0.1.0a2"

It is a pre-release until it has been confirmed against production. Naming the version like this lets pip pick the pre-release of xyp only; avoid pip install --pre xyp, which also pulls pre-releases of every dependency.

from xyp import Xyp

xyp = Xyp(access_token="...", private_key="private.key")

card = xyp.citizen.get_citizen_id_card_info(regnum="РД00000000")
print(card.firstname, card.lastname)

That is the whole integration. The SDK signs every request, builds the SOAP envelope, verifies XYP's certificate and turns the XML into typed objects.

Status: alpha. The production system is only reachable from the National Data Center VPN, so the SDK is verified differentially instead: for every operation in the WSDLs we hold, the test suite checks that the XML this SDK sends is identical to what zeep — the SOAP library behind the official samples and known-working integrations — builds from the WSDL, that responses are decoded to the same values, and that the request signature is byte-identical to the official XypSign.py. Endpoints whose WSDL is not public are covered by the same code paths but not by that comparison. Please open an issue with anything that differs in practice.

Requirements

  • Python 3.10+
  • An access token and RSA key issued by the National Data Center, and a working connection to their VPN with xyp.gov.mn in your hosts file (connection guide). The SDK cannot replace that step.

Credentials

Xyp(access_token="...", private_key="private.key")  # file path
Xyp(access_token="...", private_key=pem_text_or_bytes)  # from your secret manager
Xyp()  # XYP_ACCESS_TOKEN + XYP_PRIVATE_KEY (path)

Keep the token and key out of your source code; environment variables or a secret manager are the intended way. Neither ever appears in repr(), logs or exception messages.

Finding a service

Services are grouped by XYP endpoint, and named after the original operation in snake_case. Your editor autocompletes all of them:

Group Services Group Services
xyp.citizen 83 xyp.property 30
xyp.health 76 xyp.statistic 24
xyp.insurance 67 xyp.labor_welfare 19
xyp.government_service 54 xyp.legal_entity 19
xyp.transport 48 xyp.education 18
xyp.payment 33 xyp.tax, xyp.meta, xyp.foreign_service, xyp.private_service, xyp.pki 28

WS100101_getCitizenIDCardInfo becomes xyp.citizen.get_citizen_id_card_info. Every method's docstring starts with the original name, so searching the package for WS100101 finds it. Inputs are keyword arguments; the result is a Pydantic model (.model_dump(), .model_dump_json()). Need the type for an annotation? from xyp.models.citizen import GetCitizenIdCardInfoResponse.

Prefer the original names? Call any service by name and get the raw data:

xyp.call("WS100101_getCitizenIDCardInfo", {"regnum": "РД00000000"})  # -> dict

This also works for services XYP adds before the SDK is updated — pass endpoint="citizen-1.5.0" for those.

Async

from xyp import AsyncXyp

async with AsyncXyp(access_token="...", private_key="private.key") as xyp:
    card = await xyp.citizen.get_citizen_id_card_info(regnum="РД00000000")

Same options, same services. Create one client and reuse it — it keeps a connection pool. Use it as a context manager or call close().

Citizen approval (OTP, fingerprint, digital signature, ДАН)

Many services only answer when the citizen has approved the request:

from xyp import CitizenAuth

# 1. ask XYP to text the citizen a code for the services you are going to call
xyp.meta.register_otp_request(
    regnum=regnum,
    json_ws_list='[{"ws": "WS100101_getCitizenIDCardInfo"}]',
    is_sms=1,
    auth=CitizenAuth(regnum=regnum),
)

# 2. call the service with the code they read out to you
card = xyp.citizen.get_citizen_id_card_info(
    regnum=regnum,
    auth=CitizenAuth.with_otp(regnum=regnum, otp=123456),
)

Also available: CitizenAuth.with_sso_otp, .with_signature, .with_fingerprint, .with_dan_app, and OperatorAuth with the same constructors for services that need the operator too (operator=).

Errors

from xyp import NotFoundError, AccessDeniedError, XypApiError, XypConnectionError

try:
    card = xyp.citizen.get_citizen_id_card_info(regnum=regnum)
except NotFoundError:
    card = None  # resultCode 1: provider has no record
except AccessDeniedError:
    ...  # your token may not call this service
except XypApiError as error:
    log(error.result_code, error.message, error.request_id)
except XypConnectionError:
    ...  # VPN down, hosts entry missing, TLS, timeout
Exception resultCode
NotFoundError 1
InternalError 2
InvalidRequestError 3 (missing input, bad token / timestamp / signature header)
AuthRequiredError 200–202
AccessDeniedError 203, 501
FingerprintError 301–304
CitizenDataError 401–402
SignatureError 601–605
ProviderError 801–802

All of them are XypApiError; everything the SDK raises is an XypError. request_id is what XYP support will ask you for.

Every error has an origin, so you (and your logs) know whose side it is on:

error.origin Meaning Exceptions
"config" how the client was set up XypConfigError
"network" VPN, hosts entry, TLS, timeout XypConnectionError, XypTimeoutError
"xyp" XYP or the data provider answered with an error XypApiError and subclasses, XypResponseError
"sdk" a gap in this SDK — please report it XypValidationError

When XYP's data does not match the model

The response models are generated from XYP's public catalog, which is typed by hand, so real data can disagree with them. That never costs you a call:

card = xyp.citizen.get_citizen_id_card_info(regnum=regnum)  # succeeds
card.firstname  # everything that fits is typed as usual
card.birth_date  # None: this field did not fit
card.xyp_mismatches  # (Mismatch(path="birthDate", problem="..."),) with the raw .value

You also get an XypModelMismatchWarning pointing at your line, naming the field and saying plainly that the SDK's model is wrong — not XYP, not your code. It contains field names only, never values, so citizen data stays out of your logs. Silence it with warnings.simplefilter("ignore", XypModelMismatchWarning) or turn it into an error in your tests. XypValidationError is reserved for the rare response that cannot be fitted at all; its .data still holds the parsed response.

TLS

XYP's certificate is issued by the Mongolian national CA, which no operating system trusts by default — the reason most integrations (including the official samples) switch verification off. This SDK bundles the national root and issuing CA instead and verifies against exactly those.

Xyp(..., verify=True)  # default: bundled national CAs
Xyp(..., verify="/path/to/ca.pem")  # your own CA bundle
Xyp(..., verify=ssl_context)  # full control
Xyp(..., verify=False)  # off — read docs/tls.md first

Going through your own HTTPS proxy? Set base_url="https://proxy.example" and a verify that matches the proxy's certificate. Details and the reasoning: docs/tls.md.

Types

  • Every response field is optional: providers leave fields out freely.
  • Unknown fields are kept (model.model_extra) instead of raising.
  • Date fields are datetime when XYP sends ISO 8601 and the original str otherwise; formats are not documented, so nothing is rejected.
  • byte[] fields (photos, PDFs) arrive as bytes.

Development

uv sync
uv run pytest
uv run pyright
uv run ruff check .

src/xyp/operations (one module per service), src/xyp/services, src/xyp/models, _groups.py and _operations.py are generated — change generator/ or spec/ and run uv run xyp-generate from generator/ instead of editing them.

License

MIT

Release files for xyp 0.1.0a2

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

Source distribution (sdist)

Source distribution for xyp 0.1.0a2
File Size Uploaded
xyp-0.1.0a2.tar.gz 338.6 kB Details

Built distribution (wheel)

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

Total release size: 1.1 MB

Release files / xyp-0.1.0a2.tar.gz

Download URL xyp-0.1.0a2.tar.gz
Size 338.6 kB
Tags Source
SHA-256 checksum
How to use checksums
9b91e13b0eb4df1e9c596020947b570f61e2677c55f9f59b0b880a12c1e30c33
BLAKE2b-256 checksum
How to use checksums
bf66f0a51baecd5803ada15cee7dc4f445d7cb18cbb355406df0e6b22d551b5d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.

Transparency log

Release files / xyp-0.1.0a2-py3-none-any.whl

Download URL xyp-0.1.0a2-py3-none-any.whl
Size 731.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
86ce846f647188c312806093bffecb95000cfc88afb6e0037949df37d26b7034
BLAKE2b-256 checksum
How to use checksums
518f15e461a6d9218e2e231137cf7ad843665805ec7f91a427a72c51896212a9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0a2 This release

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