keyban_api_client
Keyban API Client
Python client for the Keyban DPP Passport API.
keyban_api_client.content_hash
felt252-masked content hash of the CLEAR passport data, anchored on-chain for the verify-dpp drag-and-drop verification.
FROZEN PROTOCOL (ADR 0080): this algorithm is an immutable verification contract shared with the backend and the frozen verify-dpp bundle. A published version of this client embeds it as-of release — changing it silently breaks already-delivered Adomos clients. See docs/dpp/protocol/content-hash-v1.json.
CONTENT_HASH_PATTERN
0x + 64 lowercase hex, first byte masked & 0x07 (Starknet felt252).
compute_content_hash
def compute_content_hash(data: Dict[str, Any]) -> str
Hash of the CLEAR data blob: JCS (RFC 8785) of \{"type": "Data", "data": data\} — the shape the verify-dpp drop zone rebuilds — then
SHA-256 with the top 5 bits masked (felt252). Must match byte-for-byte
backend/nestjs/src/dpp/vc/content-hash.ts and
sdk/apps/verify-dpp/src/lib/content-hash.ts.
Only mirrors the backend subject for items with a bare UNTP product
(what create_passport_item sends).
Raises:
ValueError- NaN/Infinity floats or ints beyond +/-(2**53 - 1) — use strings for large numeric identifiers.
keyban_api_client.client
Keyban API Client
Python client for the Keyban DPP Passport API. Writes are item-granularity
(the level that certifies the data blob on-chain); reads remain agnostic.
KeybanAPIError Objects
class KeybanAPIError(requests.HTTPError)
API error with structured details from the response body.
Attributes:
-
status_code- HTTP status code -
detail- Parsed JSON body. The backend emits RFC 7807 ProblemDetails ({status, title, detail, type, instance}); validation errors also include anerrorsarray.str(err) renders as
"HTTP \{code\} \{title\}: \{detail\}"so a simpleprint(err)gives the full diagnostic without digging into attributes.
PassportData Objects
class PassportData(BaseModel)
Dynamic passport data fields with optional field-level encryption.
Automatically converts Python date/datetime objects to ISO string format.
Example:
from datetime import date
data = PassportData( name="My product", manufacturing_date=date(2024, 6, 15), # auto -> "2024-06-15" )
create_encrypted
@classmethod
def create_encrypted(cls,
confidential_paths: Optional[List[str]] = None,
enc_algorithm: str = "sha256",
enc_key: Optional[str] = None,
**data) -> "PassportData"
Build PassportData with selected fields hashed or encrypted.
Arguments:
-
confidential_paths- Dot-notation paths to protect. -
enc_algorithm- "sha256" (one-way hash) or "aes-256-gcm" (reversible). -
enc_key- Base64-encoded 32-byte key; auto-generated if None for AES. -
**data- Passport data fields. -
WARNING- SHA256 is irreversible. Use "aes-256-gcm" if you need decryption.
encryption_key
@property
def encryption_key() -> Optional[str]
Return the encryption key used (if any).
content_hash
@property
def content_hash() -> str
Hash of the CLEAR data (computed before encryption, None fields
excluded as in model_dump) — pass it as content_hash to the
item write methods.
clear_data
@property
def clear_data() -> Dict[str, Any]
The clear blob whose hash is anchored — serialize it to JSON and hand it to the end user for verify-dpp drag-and-drop.
Passport Objects
class Passport(BaseModel)
Keyban DPP Passport response (model / batch / item).
PassportListResponse Objects
class PassportListResponse(BaseModel)
Paginated list response.
FilterOperator Objects
class FilterOperator(BaseModel)
Filter operator for list_passports.
The value must already be a string in the format the backend expects for the target field (e.g. ISO 8601 for datetime fields, UUID string for application.id, "true"/"false" for booleans). See the backend field schema.
operator
'eq', 'contains', 'gt', 'lt', 'gte', 'lte', 'ne'
PassportClient Objects
class PassportClient()
Python client for the Keyban DPP Passport API.
Read operations (list_passports, get_passport) are agnostic and return
passports of any granularity. Write operations are item-granularity
(create_passport_item / update_passport_item).
list_passports
def list_passports(filters: Optional[List[FilterOperator]] = None,
current_page: int = 1,
page_size: int = 10) -> PassportListResponse
List passports with optional filters and pagination.
get_passport
def get_passport(passport_id: UUID) -> Passport
Get a passport by ID.
create_passport_item
def create_passport_item(
*,
application: UUID,
network: str,
item_number: str,
model_number: Optional[str] = None,
product_name: Optional[str] = None,
data: Optional[Dict[str, Any]] = None,
certified_paths: Optional[List[str]] = None,
content_hash: Optional[str] = None,
certificate_uri: Optional[str] = None,
certificate_signature: Optional[str] = None) -> Passport
Create an item-granularity passport (items certify their data
blob on-chain; models certify the product definition only).
content_hash (PassportData.content_hash or
compute_content_hash) is anchored verbatim in the on-chain event
so holders of the clear certificate can verify encrypted passports;
omitted → the backend anchors its own hash. It always covers the FULL
clear blob, while certified_paths filters the signed VC only — the
reserved certificate.opt_in_form path is auto-kept when data
carries the form, so the opt-in form is never dropped from the VC.
model_number links the item to its parent model. No claim
parameters: items created here are never minted.
To self-host a (deletable) certificate, prefer
:meth:prepare_self_hosted_item, which builds the credential to host and
reuses the exact same content here. certificate_uri /
certificate_signature are the low-level hooks it relies on: when set,
the backend anchors the URI + signature on-chain instead of uploading the
credential to IPFS.
Raises:
ValueError- invalidcontent_hashformat (before any network call).
update_passport_item
def update_passport_item(
passport_id: UUID,
*,
data: Optional[Dict[str, Any]] = None,
certified_paths: Optional[List[str]] = None,
content_hash: Optional[str] = None,
certificate_uri: Optional[str] = None,
certificate_signature: Optional[str] = None) -> Passport
Update an item-granularity passport.
WARNING: when changing data, pass the matching content_hash
in the SAME call (derive it from the data you send — a stale hash is
undetectable server-side); without a hash the backend falls back to
its server-computed hash. A hash-only call re-certifies on-chain.
A reserved certificate.opt_in_form path is auto-added to a non-empty
certified_paths when data carries the form, so it is never
dropped from the VC. Caveat: if you narrow certified_paths WITHOUT
resending data, add certificate.opt_in_form yourself.
Raises:
ValueError- invalidcontent_hashformat (before any network call).
upload_opt_in_form
def upload_opt_in_form(file_path: Optional[str] = None,
*,
content: Optional[bytes] = None,
media_type: str = "image/png",
filename: Optional[str] = None) -> str
Upload an opt-in form screenshot to IPFS; return its ipfs://<cid> URI.
Provide exactly one of file_path or raw content bytes. The returned
URI is a plain certificate data field: store it alongside the other Adomos
certificate fields, i.e. data["certificate"]["opt_in_form"] on
create_passport_item / update_passport_item. Add it BEFORE deriving
content_hash (include it in the PassportData you hash) so the
anchored hash — which always covers the full clear blob — protects the form
too. The verify-dpp page then reads it from certificate.opt_in_form.
Arguments:
file_path- Path to the image file to read.content- Raw image bytes (alternative tofile_path).media_type- One ofimage/png,image/jpeg,image/webp.filename- Name recorded with the pin; defaults to the basename offile_pathoropt-in-form.
prepare_self_hosted_item
def prepare_self_hosted_item(
*,
application: UUID,
network: str,
item_number: str,
model_number: Optional[str] = None,
product_name: Optional[str] = None,
data: Optional[Dict[str, Any]] = None,
certified_paths: Optional[List[str]] = None,
content_hash: Optional[str] = None) -> "SelfHostedDraft"
Prepare a self-hosted (deletable) certificate for an item passport.
Same arguments as :meth:create_passport_item. Builds and signs — via
POST /v1/dpp/passports/build-vc — the exact VC the platform would
otherwise upload to IPFS, WITHOUT persisting or anchoring anything, and
returns a :class:SelfHostedDraft. Then:
- host
draft.certificateat your own https URL; - call
draft.create(certificate_uri=...)(ordraft.update(passport_id, certificate_uri=...)).
The content captured here is reused verbatim when you finalize, so the
signed credential and the on-chain content hash match. Encryption,
content_hash and the reserved opt-in-form path are handled exactly as
in :meth:create_passport_item.
close
def close()
Close the HTTP session.
SelfHostedDraft Objects
class SelfHostedDraft()
A signed certificate awaiting self-hosting before an item passport write.
Returned by :meth:PassportClient.prepare_self_hosted_item. Host
:attr:certificate at your own https URL, then call :meth:create (or
:meth:update) with that URL — the passport content captured when preparing
the draft is reused verbatim, so the on-chain record pins the hosted VC.
Attributes:
certificate- The signed Verifiable Credential (JSON-LD) to host.
create
def create(*, certificate_uri: str) -> Passport
Create the passport, anchoring the self-hosted certificate_uri.
update
def update(passport_id: UUID, *, certificate_uri: str) -> Passport
Update an existing passport to point at the self-hosted credential.