mk_live_… key below (it stays in this browser only and is sent
as a Bearer header, never as a cookie), then run a real /mask call.
The encrypted envelope is decrypted in your browser with WebCrypto, so
you see the recovered mappings and the reconstructed original text.MRN is an extended entity type; enable it under
Settings → Entities (or PUT /me/entities) before this example detects
it. On a new account only the ten core types are on, so the
MRN H123456 below would pass through unmasked. See
PII types.123456789101112131415161718192021222324252627282930313233343536373839import base64 import json import requests from cryptography.hazmat.primitives.ciphers.aead import AESGCM from cryptography.hazmat.primitives.kdf.hkdf import HKDF from cryptography.hazmat.primitives import hashes API = "https://api.kindi.me" KEY = "mk_live_..." resp = requests.post( f"{API}/api/v1/mask", headers={"Authorization": f"Bearer {KEY}"}, json={"text": "Patient John Doe, DOB 1980-01-01, MRN H123456"}, ).json() # Derive the key-encryption-key from the bearer string. kek = HKDF( algorithm=hashes.SHA256(), length=32, salt=b"masker-kek-salt-v1", info=b"masker-kek-v1", ).derive(KEY.encode()) # Unwrap the per-request DEK. dek = AESGCM(kek).decrypt( base64.b64decode(resp["nonce_dek"]), base64.b64decode(resp["wrapped_dek"]), None, ) # Decrypt the mappings payload. plain = AESGCM(dek).decrypt( base64.b64decode(resp["nonce_payload"]), base64.b64decode(resp["ciphertext"]), None, ) print(resp["masked_text"]) print(json.loads(plain.decode()))
masker-kek-salt-v1) and info (masker-kek-v1) into a 32-byte
KEK (key-encryption-key). The server returned a wrapped_dek;
that's a per-request DEK (data-encryption-key) wrapped under the
KEK. Unwrap it, then decrypt the ciphertext to get back a JSON
mapping of every <MASKED_TYPE_id> token to its original value./unmask endpoint.