{"schema_version":"lending-cred-issue-1","endpoint":"POST /api/lending/credential/issue","discovery":"GET /api/lending/credential/issue","auth":{"scheme":"Public endpoint; signing identity is server-owned (Ed25519).","issuer_env_var":"LENDING_ISSUER_PRIVATE_KEY","notes":"When LENDING_ISSUER_PRIVATE_KEY is unset the endpoint still works but issues vouchers signed with an ephemeral key (mock_key: true). Vouchers signed in mock mode do not verify across restarts."},"canonicalization":{"rule":"JSON.stringify(obj) with keys sorted alphabetically and no whitespace.","algorithm":"Builds the structured payload then renders it to a single UTF-8 string. That string is what the issuer signs. The payload_string returned in the response mirrors bytes-on-the-wire so a verifier using the same rule reproduces them exactly."},"determinism":{"rule":"voucher_id = \"vch_<sha256[:32]>\" over (user_pubkey + \":\" + claim_id + \":\" + ciphertext_hash + \":\" + floor(issued_at_ms / (validity_seconds * 1000)) + \":\" + issuer).","replay_window":"floored by the client-supplied validity_seconds; same input within the same window → same voucher_id, same signature (no DB write, replay: true in the response)."},"content_type":"application/json","required_fields":{"user_pubkey":{"type":"string","min_length":1,"max_length":255,"description":"Borrower public key identifier — the project's chosen \"deterministic per-user ID\" field. Used as the schema-level partition for the voucher and replay lookup."},"claim_id":{"type":"string","min_length":1,"max_length":120,"description":"Borrower-supplied claim identifier (e.g. \"clm_01J9Q\")."},"ciphertext":{"type":"string","min_length":1,"max_bytes":65536,"description":"Opaque encrypted financial-history bytes. The server treats this as opaque and never decrypts; it is hashed and persisted verbatim for audit."},"validity_seconds":{"type":"integer","min":60,"max":86400,"optional_if":"valid_until","description":"Time-bounded voucher lifetime in seconds (60…86400). Mutually exclusive with valid_until."},"valid_until":{"type":"string","format":"ISO-8601","optional_if":"validity_seconds","description":"Absolute expiry timestamp. Server clamps to issued_at + 24h."}},"responses":{"200":{"ok":"true","voucher_id":"string (vch_<sha256[:32]>)","user_pubkey":"string","claim_id":"string","issuer":"string","issued_at":"ISO-8601 string","valid_until":"ISO-8601 string","signature":"string (128-hex Ed25519)","payload_string":"string (canonical UTF-8 bytes the issuer signed)","ciphertext_hash":"string (64-hex sha256 over the opaque ciphertext)","determinism":{"replay_window_seconds":"integer"},"issuer_pub_key_pem":"string (PEM SPKI Ed25519)","mock_key":"boolean","replay":"boolean — true if this is a same-window replay (no DB write)"},"400":{"ok":"false","errors":["string"]}},"example_envelope":{"user_pubkey":"pk_loan_4f7e2c1a","claim_id":"clm_01J9Q","ciphertext":"0x9f3a8c1d_demo_ciphertext_octets","validity_seconds":3600},"persistence":{"table":"lending_credentials","deterministic_key":"voucher_id (UNIQUE)","indexes":["lending_credentials_user_pubkey_idx","lending_credentials_valid_until_idx"]}}