Skip to main content

Official ValidPay Python SDK — client-side AES-256-GCM encryption + ValidPay API client

Project description

ValidPay Python SDK

Official Python SDK for the ValidPay document verification API. Provides client-side AES-256-GCM encryption and a thin client around the ValidPay HTTP API.

The encryption format is wire-compatible with the Node.js SDK: a payload encrypted by the Python SDK can be decrypted by the Node SDK and vice versa.

Install

pip install validpay

For physical-binding support (Patent F — image-based binding zones), install the optional extras:

pip install validpay[binding]

Requires Python 3.9+.

Quick start

from validpay import ValidPayClient

client = ValidPayClient(api_key="vp_live_xxx")

# Create a single intent — the payload is encrypted locally before
# anything leaves your process. Only the ciphertext is sent to ValidPay.
result = client.create_intent(
    document_type="check",
    payload={"payee": "John Doe", "amount": 1500.00, "check_number": "10042"},
)
print(result.retrieval_id)  # vp_abc123def456
print(result.key)           # base64 AES-256 key — deliver out-of-band

# Create up to 100 intents in one round trip.
results = client.create_intent_batch([
    {"document_type": "check", "payload": {"payee": "Alice", "amount": 500}},
    {"document_type": "check", "payload": {"payee": "Bob",   "amount": 750}},
])
for r in results:
    print(r.retrieval_id, r.key)

# Verify (retrieve + decrypt). No API key required for this endpoint.
verification = client.verify_intent(
    retrieval_id="vp_abc123def456",
    key=result.key,
)
print(verification.payload)         # decrypted dict
print(verification.issuer)          # "Acme Corp"
print(verification.issuer_verified) # True
print(verification.status)          # "active"

Time-Locked Verification (Patent D)

Restrict when a document can be verified by specifying a validity window:

from datetime import datetime, timezone, timedelta

result = client.create_intent(
    document_type="check",
    payload={"payee": "Jane Doe", "amount": 1500.00},
    valid_from=(datetime.now(timezone.utc) + timedelta(hours=1)).isoformat(),
    valid_until=(datetime.now(timezone.utc) + timedelta(days=30)).isoformat(),
)

# Later, when verifying:
verified = client.verify_intent(result.retrieval_id, result.key)
print(verified.time_lock_status)  # "valid", "not_yet_valid", or "expired"
print(verified.valid_from)        # ISO-8601 timestamp or None
print(verified.valid_until)       # ISO-8601 timestamp or None

Time-lock status is informational — the SDK always returns the decrypted payload regardless of the time window. Your application decides how to handle not_yet_valid or expired results. The server stores the timestamps but never enforces them; this preserves the blind intermediary model (the server never decides whether a document is "still good").

The same valid_from / valid_until keyword arguments are accepted by create_intent_batch (per-item), create_split_key_intent, and create_selective_intent.

Split-key intents (Patent C)

Splits the AES key into two XOR shares: Share A is returned to the caller (typically embedded in the QR code), Share B is stored server-side. Neither share alone can decrypt the payload.

result = client.create_split_key_intent(
    document_type="ssn_card",
    payload={"ssn": "123-45-6789"},
)
# result.key is Share A — pair it with Share B at verification time.

verified = client.verify_split_key_intent(result.retrieval_id, result.key)
print(verified.payload)

Selective disclosure (Patent E)

Each field is encrypted with its own per-field key. A disclosure policy maps role names to the fields that role may decrypt. A full role with access to every field is added automatically.

result = client.create_selective_intent(
    document_type="check",
    payload={"payee": "Alice", "amount": 1500.00, "memo": "rent"},
    disclosure_policy={"bank": ["amount"], "auditor": ["amount", "payee"]},
)

# Bank sees only 'amount'; other fields come back as REDACTED markers.
verified = client.verify_selective_intent(result.retrieval_id, result.key, role="bank")
print(verified.payload)

create_selective_intent accepts split_key=True to combine Patents C + E.

Revocation (Patent H — Blind Revocation)

Issuers can revoke or reinstate an intent without decrypting it. Verifiers of a revoked intent receive status="revoked" and no encrypted payload.

client.revoke_intent("vp_abc123def456", reason="Stop payment")
client.reinstate_intent("vp_abc123def456", reason="False alarm")

history = client.get_revocation_history("vp_abc123def456")
for event in history:
    print(event["action"], event["reason"], event["performed_at"])

API

ValidPayClient(api_key, *, base_url=..., timeout=30.0, session=None)

  • api_key — your ValidPay API key (required for create endpoints).
  • base_url — defaults to https://api.validpay.com.
  • timeout — per-request timeout in seconds.
  • session — optionally provide a requests.Session for connection pooling, custom adapters, or mocking in tests.

client.create_intent(document_type, payload) -> CreateIntentResult

Encrypts payload (any JSON-serializable value) under a freshly generated AES-256 key and registers it with ValidPay. Returns the retrieval id and the key. The key is never sent to ValidPay — you must hand it off out-of-band to whoever needs to verify the intent.

client.create_intent_batch(intents) -> list[CreateIntentResult]

Bulk version. intents is an iterable of mappings shaped {"document_type": str, "payload": Any}, with 1–100 items. Each intent gets its own unique key. Result order matches input order.

client.verify_intent(retrieval_id, key) -> VerifyIntentResult

Fetches the intent (public endpoint, no API key required), decrypts the payload locally, and returns issuer metadata + the decrypted payload.

Errors

All SDK and API errors are raised as ValidPayError, which exposes:

  • code — machine-readable code (e.g. "unauthorized", "not_found", "decryption_failed", "invalid_key", "network_error").
  • status — HTTP status when the error came from the API.
  • details — raw error body / extra context when available.

Low-level crypto

For advanced use cases the encryption primitives are exported directly:

from validpay import generate_key, encrypt, decrypt

key = generate_key()
blob = encrypt('{"hello": "world"}', key)
assert decrypt(blob, key) == '{"hello": "world"}'

Wire format: base64(iv[12] || authTag[16] || ciphertext).

Development

pip install -e ".[dev]"
pytest

License

MIT — see LICENSE.

Project details


Download files

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

Source Distribution

validpay-1.0.1.tar.gz (24.5 kB view details)

Uploaded Source

Built Distribution

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

validpay-1.0.1-py3-none-any.whl (23.8 kB view details)

Uploaded Python 3

File details

Details for the file validpay-1.0.1.tar.gz.

File metadata

  • Download URL: validpay-1.0.1.tar.gz
  • Upload date:
  • Size: 24.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for validpay-1.0.1.tar.gz
Algorithm Hash digest
SHA256 02dfb8e5eccae656cb9fa65cc2774a9bc700b4f94835947f0717c5efbe56d34e
MD5 76d9505b86ada35142a9078a23ec332b
BLAKE2b-256 fe2ec7950baaebb9bf6fe4ee8c2042db77acf1e88be702c936f04136b7393e6d

See more details on using hashes here.

Provenance

The following attestation bundles were made for validpay-1.0.1.tar.gz:

Publisher: publish.yml on ValidPay-io/validpay-python-sdk

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

File details

Details for the file validpay-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: validpay-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 23.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for validpay-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d7b5969f6badcce67faa4a8f6296f9239a97313e807158cc3e168c6380ad96d9
MD5 c827fe78c273bf663b877b6ac3fb7ff6
BLAKE2b-256 710a95d8398bc66ec5fccf7f7fbb0c527b45daa48d7d544275bba23fea8c1271

See more details on using hashes here.

Provenance

The following attestation bundles were made for validpay-1.0.1-py3-none-any.whl:

Publisher: publish.yml on ValidPay-io/validpay-python-sdk

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

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page