hashsign-python
Official Python SDK for HashSign — hash documents locally, request cryptographic verification payloads, and retrieve Hedera Hashgraph audit proofs. Designed for AI agents that need programmatic, auditable signing.
API reference: hashsign.io/docs.
Requires Python 3.10+.
Install
pip install hashsign-python
From this monorepo:
cd sdks/python
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
Quickstart
from hashsign import HashSignClient
client = HashSignClient(api_key="hs_live_...")
document = client.documents.upload("contract.pdf")
signed = client.documents.sign(document.id, metadata={"agentId": "invoice-bot"})
proof = client.documents.verify(document.id)
print(signed.payload.signature, signed.document.status)
print(proof.hedera_transaction_id, proof.verified)
sign() returns the full SignDocumentResponse from the API — .payload is the detached signature, .document reflects the new status.
Hybrid ML-DSA-65 envelope
Pass pqc_mode=True when the API key has requirePqcSignatures or when you want a hybrid-ecdsa-ml-dsa envelope. The post-quantum half is FIPS 204 ML-DSA-65 over the raw 32-byte SHA-256 digest (pure ML-DSA, not HashML-DSA). The signature is 3309 bytes. Hedera Consensus Service stores only the 32-byte hybrid anchor, because topic messages are capped at 1024 bytes. The landing-page example topic is 0.0.9001; live topics come from the proof's topic_id.
signed = client.documents.sign(document.id, pqc_mode=True)
proof = client.documents.verify(document.id)
Recompute the anchor locally and compare it to the HCS message field hybridAnchor:
import hashlib
payload = signed.payload
anchor = hashlib.sha256(
f"{payload.signature}{payload.pqc.signature}".encode()
).hexdigest()
assert anchor == payload.pqc.hybrid_anchor
payload.pqc.public_key is the 1952-byte ML-DSA-65 public key (hex). Verify the signature over the raw digest bytes with any FIPS 204 implementation. The Python SDK requests the envelope; it does not bundle a lattice library.
Async
import asyncio
from hashsign import AsyncHashSignClient
async def main() -> None:
async with AsyncHashSignClient(api_key="hs_live_...") as client:
document = await client.documents.upload("contract.pdf")
await client.documents.sign(document.id)
proof = await client.documents.verify(document.id)
print(proof.network, proof.verified)
asyncio.run(main())
Rotate agent credentials
rotated = client.keys.rotate()
print(rotated.key.secret) # shown once — persist immediately
Configuration
| Argument | Default | Description |
|---|---|---|
api_key |
required | hs_live_... or hs_test_... |
base_url |
https://api.hashsign.io/v1 |
API origin including /v1 |
timeout |
30.0 |
httpx timeout (seconds) |
transport |
None |
Custom httpx transport (tests) |
Errors
All failures — HTTP status errors, transport failures, and malformed responses — subclass HashSignError, so one except HashSignError boundary is sufficient:
AuthenticationError— invalid or revoked key (401 / 403)ValidationError— request / hash mismatch (400 / 422), or malformed local metadataRateLimitError— includesretry_after_seconds(429)HederaNetworkError— consensus or mirror-node outage (502 / 503)NotFoundError/ConflictErrorAPIConnectionError— DNS, refused connection, TLS; wrapshttpx.TransportErrorAPITimeoutError— subclass ofAPIConnectionErrorfor connect / read timeoutsInvalidResponseError— 2xx body that is not JSON, empty, or fails schema validation
In the async client, file hashing runs in a worker thread (asyncio.to_thread) so large uploads never block the event loop.
Local crypto
hashsign.crypto hashes files with SHA-256 and can produce detached Ed25519 signatures. Production deployments should swap those helpers for Hedera operator-key signing (hedera-sdk-py) at the marked HOOK comments.
Tests
pytest
The suite uses httpx.MockTransport and does not call a live API or Hedera network. tests/test_contract.py additionally validates every request body the SDK emits against packages/shared/openapi.yaml, so SDK/spec drift fails CI.
Metadata
Release files for hashsign-python 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| hashsign_python-0.1.1.tar.gz | 16.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hashsign_python-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 31.0 kB
Release files / hashsign_python-0.1.1.tar.gz
| Download URL | hashsign_python-0.1.1.tar.gz |
|---|---|
| Size | 16.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e63bdb5bbc3189d5f65769d7d646135afb988e24bcdc01b91d06003d5eb2efb0
|
|
BLAKE2b-256 checksum How to use checksums |
4f239c51af6babee3b1ed22a690c2ab9a18517d5cd9d2ee4b8720aaad3d56c07
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|
Release files / hashsign_python-0.1.1-py3-none-any.whl
| Download URL | hashsign_python-0.1.1-py3-none-any.whl |
|---|---|
| Size | 14.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3e8cb390e71531519a1e4f74bdd34dda7f47d7c4af5a2fd647e2f18af1cb40da
|
|
BLAKE2b-256 checksum How to use checksums |
cecbf06e49f8695c1c845e6f7a5e81c6d8204c83fd0dcefe573775d496b569af
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|