Skip to main content

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 an errors array.

    str(err) renders as "HTTP \{code\} \{title\}: \{detail\}" so a simple print(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 - invalid content_hash format (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 - invalid content_hash format (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 to file_path).
  • media_type - One of image/png, image/jpeg, image/webp.
  • filename - Name recorded with the pin; defaults to the basename of file_path or opt-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:

  1. host draft.certificate at your own https URL;
  2. call draft.create(certificate_uri=...) (or draft.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.