Agent-readable docs index: /llms.txt. Full docs in one file: /llms-full.txt. Download /docs.zip to grep all markdown files locally.
This example takes a clinical note, sends it to KINDI, and decrypts the encrypted envelope client-side using your API key. It's the canonical flow: every other example in this section builds on it.

Try it live

Paste a 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.
Try it · /api/v1/mask
87 chars
Set your API key in the bar above to run this.
Copy as
The static SDK snippets below are the same flow in code; copy whichever language you need.

1. Get an API key

Mint one at the API keys page.
For local development, mint a playground key on that same page: playground keys skip billing entirely, so they don't deplete your token balance. They can only be created from the dashboard while signed in — there is no request field or API parameter that turns an ordinary key into a playground key. They also carry a lower default rate limit (30 rpm rather than 60); see Rate limits.
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.

2. The full round-trip

import 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()))

3. What's happening

The bearer string is stretched through HKDF-SHA256 with a fixed salt (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.
Revoking the API key makes any outstanding envelope responses permanently undecryptable, by design. There is no server-side mapping store and no /unmask endpoint.